Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-python-box for 
openSUSE:Factory checked in at 2022-06-18 22:06:24
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-python-box (Old)
 and      /work/SRC/openSUSE:Factory/.python-python-box.new.1548 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-python-box"

Sat Jun 18 22:06:24 2022 rev:7 rq:983572 version:6.0.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-python-box/python-python-box.changes      
2021-12-09 19:46:21.449154866 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-python-box.new.1548/python-python-box.changes
    2022-06-18 22:06:28.271680092 +0200
@@ -1,0 +2,9 @@
+Fri Jun 17 12:20:56 UTC 2022 - Mark??ta Machov?? <[email protected]>
+
+- Update to 6.0.2
+  * Adding Cython support to greatly speed up normal Box operations on 
supported systems
+  * Adding #196 support for sliceable boxes
+  * Changing #208 __repr__ to produce eval-able text
+  * Removing support for ruamel.yaml < 0.17
+
+-------------------------------------------------------------------

Old:
----
  5.4.1.tar.gz

New:
----
  6.0.2.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-python-box.spec ++++++
--- /var/tmp/diff_new_pack.EFaE5e/_old  2022-06-18 22:06:28.883680961 +0200
+++ /var/tmp/diff_new_pack.EFaE5e/_new  2022-06-18 22:06:28.887680966 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-python-box
 #
-# Copyright (c) 2021 SUSE LLC
+# Copyright (c) 2022 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -20,7 +20,7 @@
 # python_requires='>=3.6'
 %define skip_python2 1
 Name:           python-python-box
-Version:        5.4.1
+Version:        6.0.2
 Release:        0
 Summary:        Advanced Python dictionaries with dot notation access
 License:        MIT
@@ -34,12 +34,12 @@
 # SECTION test requirements
 BuildRequires:  %{python_module msgpack >= 1.0.0}
 BuildRequires:  %{python_module pytest}
-BuildRequires:  %{python_module ruamel.yaml >= 0.16.10}
-BuildRequires:  %{python_module toml >= 0.10.1}
+BuildRequires:  %{python_module ruamel.yaml >= 0.17}
+BuildRequires:  %{python_module toml >= 0.10.2}
 # /SECTION
 Requires:       python-msgpack >= 1.0.0
-Requires:       python-ruamel.yaml >= 0.16.10
-Requires:       python-toml >= 0.10.1
+Requires:       python-ruamel.yaml >= 0.17
+Requires:       python-toml >= 0.10.2
 %python_subpackages
 
 %description

++++++ 5.4.1.tar.gz -> 6.0.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/.github/workflows/pythonpublish.yml 
new/Box-6.0.2/.github/workflows/pythonpublish.yml
--- old/Box-5.4.1/.github/workflows/pythonpublish.yml   2021-08-22 
17:06:26.000000000 +0200
+++ new/Box-6.0.2/.github/workflows/pythonpublish.yml   2022-04-02 
04:24:21.000000000 +0200
@@ -8,7 +8,7 @@
     types: [created]
 
 jobs:
-  deploy:
+  deploy-generic:
 
     runs-on: ubuntu-latest
 
@@ -29,3 +29,54 @@
       run: |
         python setup.py sdist bdist_wheel
         twine upload dist/*
+
+  deploy-cython:
+    strategy:
+      matrix:
+        os: [macos-latest, windows-latest]
+        python-version: ["3.7", "3.8", "3.9", "3.10"]
+    runs-on: ${{ matrix.os }}
+
+    steps:
+    - uses: actions/checkout@v2
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v1
+      with:
+        python-version: ${{ matrix.python-version }}
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip
+        pip install setuptools wheel twine Cython --upgrade
+    - name: Build and publish
+      env:
+        TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
+        TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
+      run: |
+        python setup.py bdist_wheel
+        twine upload dist/*
+
+  deploy-cython-manylinux:
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v2
+    - name: Set up Python 3.10
+      uses: actions/setup-python@v1
+      with:
+        python-version: "3.10"
+
+    - uses: RalfG/[email protected]_x86_64
+      with:
+        python-versions: 'cp37-cp37m cp38-cp38 cp39-cp39 cp310-cp310'
+        build-requirements: 'cython'
+
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip
+        pip install twine --upgrade
+
+    - name: Publish
+      env:
+        TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
+        TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
+      run: |
+        twine upload dist/*-manylinux*.whl
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/.github/workflows/tests.yml 
new/Box-6.0.2/.github/workflows/tests.yml
--- old/Box-5.4.1/.github/workflows/tests.yml   2021-08-22 17:06:26.000000000 
+0200
+++ new/Box-6.0.2/.github/workflows/tests.yml   2022-04-02 04:24:21.000000000 
+0200
@@ -10,24 +10,28 @@
     branches: [ master, development, develop, test, tests ]
 
 jobs:
-  package_checks:
-    runs-on: ubuntu-latest
+  package-checks:
     strategy:
       matrix:
-        python-version: [3.9]
-
+        python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "pypy-3.8"]
+        os: [ubuntu-latest, macos-latest, windows-latest]
+    runs-on: ${{ matrix.os }}
     steps:
       - uses: actions/checkout@v2
       - name: Set up Python ${{ matrix.python-version }}
-        uses: actions/setup-python@v1
+        uses: actions/setup-python@v2
         with:
           python-version: ${{ matrix.python-version }}
+      - uses: actions/cache@v2
+        with:
+          path: ~/.cache/pip
+          key: package-check-${{ hashFiles('requirements.txt') }}-${{ 
hashFiles('requirements-test.txt') }}
       - name: Install dependencies
         run: |
           python -m pip install --upgrade pip
           pip install -r requirements.txt
           pip install -r requirements-test.txt
-          pip install coveralls flake8 flake8-print mypy setuptools wheel twine
+          pip install coveralls flake8 flake8-print mypy setuptools wheel 
twine Cython
       - name: Lint with flake8
         run: |
           # stop the build if there are Python syntax errors, undefined names 
or print statements
@@ -36,28 +40,89 @@
           flake8 . --count --exit-zero --max-complexity=20 
--max-line-length=120 --statistics --extend-ignore E203
       - name: Run mypy
         run: mypy box
-      - name: Check distrubiton log description
+      - name: Build Wheel and check distrubiton log description
         run: |
           python setup.py sdist bdist_wheel
           twine check dist/*
+      - name: Test packaged wheel on *nix
+        if: matrix.os != 'windows-latest'
+        run: |
+          pip install dist/*.whl
+          rm -rf box
+          python -m pytest
+      - name: Test packaged wheel on Windows
+        if: matrix.os == 'windows-latest'
+        run: |
+          $wheel = (Get-ChildItem dist\*.whl | Sort lastWriteTime | 
Select-Object -last 1).Name
+          pip install dist\${wheel}
+          Remove-item box -recurse -force
+          python -m pytest
+      - name: Upload wheel artifact
+        uses: actions/upload-artifact@v2
+        with:
+          name: python_box
+          path: dist/*.whl
 
-  test:
+  package-manylinux-checks:
     runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - name: Set up Python 3.10
+        uses: actions/setup-python@v1
+        with:
+          python-version: "3.10"
+
+      - uses: actions/cache@v2
+        with:
+          path: ~/.cache/pip
+          key: package-manylinux-check-${{ hashFiles('requirements.txt') 
}}-${{ hashFiles('requirements-test.txt') }}
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install -r requirements.txt
+          pip install -r requirements-test.txt
+          pip install coveralls flake8 flake8-print mypy setuptools wheel 
twine Cython
+
+      - uses: RalfG/[email protected]_x86_64
+        with:
+          python-versions: 'cp37-cp37m cp38-cp38 cp39-cp39 cp310-cp310'
+          build-requirements: 'cython'
+
+      - name: Test packaged wheel on linux
+        run: |
+          pip install dist/*cp310-manylinux*.whl
+          rm -rf box
+          python -m pytest
+
+      - name: Upload wheel artifact
+        uses: actions/upload-artifact@v2
+        with:
+          name: python_box
+          path: dist/*-manylinux*.whl
+
+  test:
     strategy:
       matrix:
-        python-version: [3.6, 3.7, 3.8, 3.9, 3.10-dev, pypy3]
-
+        python-version: ["3.7", "3.8", "3.9", "3.10"]
+        os: [ubuntu-latest, macos-latest, windows-latest]
+    runs-on: ${{ matrix.os }}
     steps:
     - uses: actions/checkout@v2
     - name: Set up Python ${{ matrix.python-version }}
       uses: actions/setup-python@v2
       with:
         python-version: ${{ matrix.python-version }}
+    - uses: actions/cache@v2
+      with:
+        path: ~/.cache/pip
+        key: test-${{ hashFiles('requirements.txt') }}-${{ 
hashFiles('requirements-test.txt') }}
     - name: Install dependencies
       run: |
         python -m pip install --upgrade pip
         pip install -r requirements.txt
         pip install -r requirements-test.txt
+        pip install setuptools wheel Cython
+        python setup.py build_ext --inplace
     - name: Test with pytest
       env:
         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/.gitignore new/Box-6.0.2/.gitignore
--- old/Box-5.4.1/.gitignore    2021-08-22 17:06:26.000000000 +0200
+++ new/Box-6.0.2/.gitignore    2022-04-02 04:24:21.000000000 +0200
@@ -94,3 +94,6 @@
 .pypirc
 release.bat
 coverage/
+# don't upload cython files
+box/*.c
+.pytest_cache/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/.pre-commit-config.yaml 
new/Box-6.0.2/.pre-commit-config.yaml
--- old/Box-5.4.1/.pre-commit-config.yaml       2021-08-22 17:06:26.000000000 
+0200
+++ new/Box-6.0.2/.pre-commit-config.yaml       2022-04-02 04:24:21.000000000 
+0200
@@ -1,7 +1,7 @@
 repos:
--   repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v4.0.1
-    hooks:
+- repo: https://github.com/pre-commit/pre-commit-hooks
+  rev: v4.1.0
+  hooks:
     # Identify invalid files
     - id: check-ast
     - id: check-yaml
@@ -27,13 +27,32 @@
     - id: check-executables-have-shebangs
     - id: end-of-file-fixer
       exclude: ^test/data/.+
--   repo: https://github.com/ambv/black
-    rev: 21.7b0
-    hooks:
+
+- repo: https://github.com/ambv/black
+  rev: 22.1.0
+  hooks:
     - id: black
       args: [--config=.black.toml]
--   repo: https://github.com/pre-commit/mirrors-mypy
-    rev: 'v0.910'
-    hooks:
+
+- repo: local
+  hooks:
+    - id: cythonize-check
+      name: Cythonize
+      entry: python setup.py build_ext --inplace
+      language: system
+      types: [python]
+      pass_filenames: false
+    # cythonize must come before the pytest to make sure we update all c code
+    - id: pytest-check
+      name: Check pytest
+      entry: pytest
+      language: system
+      pass_filenames: false
+      always_run: true
+
+- repo: https://github.com/pre-commit/mirrors-mypy
+  rev: 'v0.931'
+  hooks:
     - id: mypy
-      additional_dependencies: [ruamel.yaml,toml,msgpack]
+      types: [python]
+      additional_dependencies: 
[ruamel.yaml,toml,msgpack,types-PyYAML,types-toml]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/CHANGES.rst new/Box-6.0.2/CHANGES.rst
--- old/Box-5.4.1/CHANGES.rst   2021-08-22 17:06:26.000000000 +0200
+++ new/Box-6.0.2/CHANGES.rst   2022-04-02 04:24:21.000000000 +0200
@@ -1,6 +1,31 @@
 Changelog
 =========
 
+Version 6.0.2
+-------------
+
+* Fixing that the typing `pyi` files were not included in the manifest (thanks 
to Julian Torres)
+
+Version 6.0.1
+-------------
+
+* Fixing #218 Box dots would not raise KeyError on bad key (thanks to Cliff 
Wells)
+* Fixing #217 wording in readme overview needed updated (thanks to Julie Jones)
+
+Version 6.0.0
+-------------
+
+* Adding Cython support to greatly speed up normal Box operations on supported 
systems
+* Adding #161 support for access box dots with `get` and checking with `in` 
(thanks to scott-createplay)
+* Adding #183 support for all allowed character sets (thanks to Giulio 
Malventi)
+* Adding #196 support for sliceable boxes (thanks to Dias)
+* Adding #164 default_box_create_on_get toggle to disable setting box variable 
on get request (thanks to ipcoder)
+* Changing #208 __repr__ to produce `eval`-able text (thanks to Jeff Robbins)
+* Changing #215 support ruamel.yaml new syntax (thanks to Ivan Pepelnjak)
+* Changing `update` and `merge_update` to not use a keyword that could cause 
issues in rare circumstances
+* Changing internal `_safe_key` logic to be twice as fast
+* Removing support for ruamel.yaml < 0.17
+
 Version 5.4.1
 -------------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/LICENSE new/Box-6.0.2/LICENSE
--- old/Box-5.4.1/LICENSE       2021-08-22 17:06:26.000000000 +0200
+++ new/Box-6.0.2/LICENSE       2022-04-02 04:24:21.000000000 +0200
@@ -1,6 +1,6 @@
 MIT License
 
-Copyright (c) 2017-2020 Chris Griffith
+Copyright (c) 2017-2022 Chris Griffith
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/MANIFEST.in new/Box-6.0.2/MANIFEST.in
--- old/Box-5.4.1/MANIFEST.in   2021-08-22 17:06:26.000000000 +0200
+++ new/Box-6.0.2/MANIFEST.in   2022-04-02 04:24:21.000000000 +0200
@@ -2,3 +2,7 @@
 include AUTHORS.rst
 include CHANGES.rst
 include box/py.typed
+include box/*.c
+include box/*.so
+include box/*.pyd
+include box/*.pyi
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/README.rst new/Box-6.0.2/README.rst
--- old/Box-5.4.1/README.rst    2021-08-22 17:06:26.000000000 +0200
+++ new/Box-6.0.2/README.rst    2022-04-02 04:24:21.000000000 +0200
@@ -23,36 +23,78 @@
 Install
 =======
 
+**Version Pin Your Box!**
+
+If you aren't in the habit of version pinning your libraries, it will 
eventually bite you.
+Box has a `list of breaking change 
<https://github.com/cdgriffith/Box/wiki/Major-Version-Breaking-Changes>`_ 
between major versions you should always check out before updating.
+
+requirements.txt
+----------------
+
+.. code:: text
+
+        python-box[all]~=6.0
+
+As Box adheres to semantic versioning (aka API changes will only occur on 
between major version),
+it is best to use `Compatible release 
<https://www.python.org/dev/peps/pep-0440/#compatible-release>`_ matching using 
the `~=` clause.
+
+Install from command line
+-------------------------
+
 .. code:: bash
 
-        pip install --upgrade python-box[all]
+        pip install python-box[all]~=6.0 --upgrade
 
-Box 5 is no longer forcing install of external dependencies such as yaml and 
toml. Instead you can specify which you want,
-for example, `all` is shorthand for:
+Install with selected dependencies
+----------------------------------
+
+Box is no longer forcing install of external dependencies such as yaml and 
toml. Instead you can specify which you want,
+for example, `[all]` is shorthand for:
 
 .. code:: bash
 
-        pip install --upgrade python-box[ruamel.yaml,toml,msgpack]
+        pip install python-box[ruamel.yaml,toml,msgpack]~=6.0 --upgrade
 
-But you can also sub out "ruamel.yaml" for "PyYAML".
+But you can also sub out `ruamel.yaml` for `PyYAML`.
 
 Check out `more details 
<https://github.com/cdgriffith/Box/wiki/Installation>`_ on installation details.
 
-Box 5 is tested on python 3.6+ and pypy3, if you are upgrading from previous 
versions, please look through
-`any breaking changes and new features 
<https://github.com/cdgriffith/Box/wiki/Major-Version-Breaking-Changes-and-New-Features>`_.
+Box 6 is tested on python 3.6+, if you are upgrading from previous versions, 
please look through
+`any breaking changes and new features 
<https://github.com/cdgriffith/Box/wiki/Major-Version-Breaking-Changes>`_.
+
+Optimized Version
+-----------------
 
+Box 6 is introducing Cython optimizations for major platforms by default.
+Loading large data sets can be up to 10x faster!
+
+If you are **not** on a x86_64 supported system you will need to do some extra 
work to install the optimized version.
+There will be an warning of "WARNING: Cython not installed, could not optimize 
box" during install.
+You will need python development files, system compiler, and the python 
packages `Cython` and `wheel`.
+
+**Linux Example:**
+
+First make sure you have python development files installed (`python3-dev` or 
`python3-devel` in most repos).
+You will then need `Cython` and `wheel` installed and then install (or 
re-install with `--force`) `python-box`.
+
+.. code:: bash
+
+        pip install Cython wheel
+        pip install python-box[all]~=6.0 --upgrade --force
 
 If you have any issues please open a github issue with the error you are 
experiencing!
 
 Overview
 ========
 
-`Box` is designed to be an easy drop in transparently replacements for
-dictionaries, thanks to Python's
-duck typing capabilities, which adds dot notation access. Any sub
-dictionaries or ones set after initiation will be automatically converted to
-a `Box` object. You can always run `.to_dict()` on it to return the object
-and all sub objects back into a regular dictionary.
+`Box` is designed to be a near transparent drop in replacements for
+dictionaries that add dot notation access and other powerful feature.
+
+There are a lot of `types of boxes 
<https://github.com/cdgriffith/Box/wiki/Types-of-Boxes>`_
+to customize it for your needs, as well as handy `converters 
<https://github.com/cdgriffith/Box/wiki/Converters>`_!
+
+Keep in mind any sub dictionaries or ones set after initiation will be 
automatically converted to
+a `Box` object, and lists will be converted to `BoxList`, all other objects 
stay intact.
 
 Check out the `Quick Start 
<https://github.com/cdgriffith/Box/wiki/Quick-Start>`_  for more in depth 
details.
 
@@ -76,7 +118,7 @@
       small_box = Box({'data': 2, 'count': 5})
       small_box.data == small_box['data'] == getattr(small_box, 'data')
 
-All dicts (and lists) added to a `Box` will be converted on lookup to a `Box` 
(or `BoxList`),
+All dicts (and lists) added to a `Box` will be converted on insertion to a 
`Box` (or `BoxList`),
 allowing for recursive dot notation access.
 
 `Box` also includes helper functions to transform it back into a `dict`,
@@ -96,7 +138,7 @@
 License
 =======
 
-MIT License, Copyright (c) 2017-2020 Chris Griffith. See LICENSE_ file.
+MIT License, Copyright (c) 2017-2022 Chris Griffith. See LICENSE_ file.
 
 
 .. |BoxImage| image:: 
https://raw.githubusercontent.com/cdgriffith/Box/master/box_logo.png
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/box/__init__.py 
new/Box-6.0.2/box/__init__.py
--- old/Box-5.4.1/box/__init__.py       2021-08-22 17:06:26.000000000 +0200
+++ new/Box-6.0.2/box/__init__.py       2022-04-02 04:24:21.000000000 +0200
@@ -2,7 +2,7 @@
 # -*- coding: utf-8 -*-
 
 __author__ = "Chris Griffith"
-__version__ = "5.4.1"
+__version__ = "6.0.2"
 
 from box.box import Box
 from box.box_list import BoxList
@@ -10,6 +10,7 @@
 from box.exceptions import BoxError, BoxKeyError
 from box.from_file import box_from_file
 from box.shorthand_box import SBox
+import box.converters
 
 __all__ = [
     "Box",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/box/box.py new/Box-6.0.2/box/box.py
--- old/Box-5.4.1/box/box.py    2021-08-22 17:06:26.000000000 +0200
+++ new/Box-6.0.2/box/box.py    2022-04-02 04:24:21.000000000 +0200
@@ -1,17 +1,16 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 #
-# Copyright (c) 2017-2020 - Chris Griffith - MIT License
+# Copyright (c) 2017-2022 - Chris Griffith - MIT License
 """
 Improved dictionary access through dot notation with additional tools.
 """
 import copy
 import re
-import string
 import warnings
-from keyword import kwlist
+from keyword import iskeyword
 from os import PathLike
-from typing import Any, Dict, Generator, List, Tuple, Union
+from typing import Any, Dict, Generator, List, Tuple, Union, Type
 
 try:
     from typing import Callable, Iterable, Mapping
@@ -131,6 +130,7 @@
     :param default_box_attr: Specify the default replacement.
         WARNING: If this is not the default 'Box', it will not be recursive
     :param default_box_none_transform: When using default_box, treat keys with 
none values as absent. True by default
+    :param default_box_create_on_get: On lookup of a key that doesn't exist, 
create it if missing
     :param frozen_box: After creation, the box cannot be modified
     :param camel_killer_box: Convert CamelCase to snake_case
     :param conversion_box: Check for near matching keys as attributes
@@ -162,6 +162,7 @@
         default_box: bool = False,
         default_box_attr: Any = NO_DEFAULT,
         default_box_none_transform: bool = True,
+        default_box_create_on_get: bool = True,
         frozen_box: bool = False,
         camel_killer_box: bool = False,
         conversion_box: bool = True,
@@ -171,7 +172,7 @@
         box_intact_types: Union[Tuple, List] = (),
         box_recast: Dict = None,
         box_dots: bool = False,
-        box_class: Union[Dict, "Box"] = None,
+        box_class: Union[Dict, Type["Box"]] = None,
         **kwargs: Any,
     ):
         """
@@ -185,6 +186,7 @@
                 "default_box": default_box,
                 "default_box_attr": cls.__class__ if default_box_attr is 
NO_DEFAULT else default_box_attr,
                 "default_box_none_transform": default_box_none_transform,
+                "default_box_create_on_get": default_box_create_on_get,
                 "conversion_box": conversion_box,
                 "box_safe_prefix": box_safe_prefix,
                 "frozen_box": frozen_box,
@@ -205,6 +207,7 @@
         default_box: bool = False,
         default_box_attr: Any = NO_DEFAULT,
         default_box_none_transform: bool = True,
+        default_box_create_on_get: bool = True,
         frozen_box: bool = False,
         camel_killer_box: bool = False,
         conversion_box: bool = True,
@@ -214,7 +217,7 @@
         box_intact_types: Union[Tuple, List] = (),
         box_recast: Dict = None,
         box_dots: bool = False,
-        box_class: Union[Dict, "Box"] = None,
+        box_class: Union[Dict, Type["Box"]] = None,
         **kwargs: Any,
     ):
         super().__init__()
@@ -224,6 +227,7 @@
                 "default_box": default_box,
                 "default_box_attr": self.__class__ if default_box_attr is 
NO_DEFAULT else default_box_attr,
                 "default_box_none_transform": default_box_none_transform,
+                "default_box_create_on_get": default_box_create_on_get,
                 "conversion_box": conversion_box,
                 "box_safe_prefix": box_safe_prefix,
                 "frozen_box": frozen_box,
@@ -331,17 +335,12 @@
         raise BoxTypeError('unhashable type: "Box"')
 
     def __dir__(self):
-        allowed = string.ascii_letters + string.digits + "_"
         items = set(super().__dir__())
         # Only show items accessible by dot notation
         for key in self.keys():
             key = str(key)
-            if " " not in key and key[0] not in string.digits and key not in 
kwlist:
-                for letter in key:
-                    if letter not in allowed:
-                        break
-                else:
-                    items.add(key)
+            if key.isidentifier() and not iskeyword(key):
+                items.add(key)
 
         for key in self.keys():
             if key not in items:
@@ -352,6 +351,21 @@
 
         return list(items)
 
+    def __contains__(self, item):
+        in_me = super().__contains__(item)
+        if not self._box_config["box_dots"] or not isinstance(item, str):
+            return in_me
+        if in_me:
+            return True
+        if "." not in item:
+            return False
+        try:
+            first_item, children = _parse_box_dots(self, item)
+        except BoxError:
+            return False
+        else:
+            return children in self[first_item]
+
     def keys(self, dotted: Union[bool] = False):
         if not dotted:
             return super().keys()
@@ -434,8 +448,9 @@
             value = default_value.copy()
         else:
             value = default_value
-        if not attr or not (item.startswith("_") and item.endswith("_")):
-            super().__setitem__(item, value)
+        if self._box_config["default_box_create_on_get"]:
+            if not attr or not (item.startswith("_") and item.endswith("_")):
+                super().__setitem__(item, value)
         return value
 
     def __box_config(self) -> Dict:
@@ -496,7 +511,7 @@
                 except BoxError:
                     if self._box_config["default_box"] and not _ignore_default:
                         return self.__get_default(item)
-                    raise
+                    raise BoxKeyError(str(item)) from _exception_cause(err)
                 if first_item in self.keys():
                     if hasattr(self[first_item], "__getitem__"):
                         return self[first_item][children]
@@ -507,6 +522,13 @@
             if self._box_config["default_box"] and not _ignore_default:
                 return self.__get_default(item)
             raise BoxKeyError(str(err)) from _exception_cause(err)
+        except TypeError as err:
+            if isinstance(item, slice):
+                new_box = self._box_config["box_class"](**self.__box_config())
+                for x in list(super().keys())[item.start : item.stop : 
item.step]:
+                    new_box[x] = self[x]
+                return new_box
+            raise BoxTypeError(str(err)) from _exception_cause(err)
 
     def __getattr__(self, item):
         try:
@@ -547,12 +569,13 @@
         self.__convert_and_store(key, value)
 
     def __setattr__(self, key, value):
-        if key != "_box_config" and self._box_config["frozen_box"] and 
self._box_config["__created"]:
+        if key == "_box_config":
+            return object.__setattr__(self, key, value)
+        if self._box_config["frozen_box"] and self._box_config["__created"]:
             raise BoxError("Box is frozen")
         if key in self._protected_keys:
             raise BoxKeyError(f'Key name "{key}" is protected')
-        if key == "_box_config":
-            return object.__setattr__(self, key, value)
+
         safe_key = self._safe_attr(key)
         if safe_key in self._box_config["__safe_keys"]:
             key = self._box_config["__safe_keys"][safe_key]
@@ -567,7 +590,10 @@
             and isinstance(key, str)
             and ("." in key or "[" in key)
         ):
-            first_item, children = _parse_box_dots(self, key)
+            try:
+                first_item, children = _parse_box_dots(self, key)
+            except BoxError:
+                raise BoxKeyError(str(key)) from None
             if hasattr(self[first_item], "__delitem__"):
                 return self[first_item].__delitem__(children)
         if key not in self.keys() and self._box_config["camel_killer_box"]:
@@ -637,7 +663,7 @@
         return key, self.pop(key)
 
     def __repr__(self) -> str:
-        return f"<Box: {self.to_dict()}>"
+        return f"Box({self})"
 
     def __str__(self) -> str:
         return str(self.to_dict())
@@ -666,21 +692,27 @@
                 out_dict[k] = v.to_list()
         return out_dict
 
-    def update(self, __m=None, **kwargs):
+    def update(self, *args, **kwargs):
         if self._box_config["frozen_box"]:
             raise BoxError("Box is frozen")
-
-        if __m:
-            if hasattr(__m, "keys"):
-                for k in __m:
-                    self.__convert_and_store(k, __m[k])
+        if (len(args) + int(bool(kwargs))) > 1:
+            raise BoxTypeError(f"update expected at most 1 argument, got 
{len(args) + int(bool(kwargs))}")
+        single_arg = next(iter(args), None)
+        if single_arg:
+            if hasattr(single_arg, "keys"):
+                for k in single_arg:
+                    self.__convert_and_store(k, single_arg[k])
             else:
-                for k, v in __m:
+                for k, v in single_arg:
                     self.__convert_and_store(k, v)
         for k in kwargs:
             self.__convert_and_store(k, kwargs[k])
 
-    def merge_update(self, __m=None, **kwargs):
+    def merge_update(self, *args, **kwargs):
+        merge_type = None
+        if "box_merge_lists" in kwargs:
+            merge_type = kwargs.pop("box_merge_lists")
+
         def convert_and_set(k, v):
             intact_type = self._box_config["box_intact_types"] and 
isinstance(v, self._box_config["box_intact_types"])
             if isinstance(v, dict) and not intact_type:
@@ -692,7 +724,6 @@
                     return
             if isinstance(v, list) and not intact_type:
                 v = box.BoxList(v, **self.__box_config())
-                merge_type = kwargs.get("box_merge_lists")
                 if merge_type == "extend" and k in self and 
isinstance(self[k], list):
                     self[k].extend(v)
                     return
@@ -703,13 +734,17 @@
                     return
             self.__setitem__(k, v)
 
-        if __m:
-            if hasattr(__m, "keys"):
-                for key in __m:
-                    convert_and_set(key, __m[key])
+        if (len(args) + int(bool(kwargs))) > 1:
+            raise BoxTypeError(f"merge_update expected at most 1 argument, got 
{len(args) + int(bool(kwargs))}")
+        single_arg = next(iter(args), None)
+        if single_arg:
+            if hasattr(single_arg, "keys"):
+                for k in single_arg:
+                    convert_and_set(k, single_arg[k])
             else:
-                for key, value in __m:
-                    convert_and_set(key, value)
+                for k, v in single_arg:
+                    convert_and_set(k, v)
+
         for key in kwargs:
             convert_and_set(key, kwargs[key])
 
@@ -730,7 +765,10 @@
 
     def _safe_attr(self, attr):
         """Convert a key into something that is accessible as an attribute"""
-        allowed = string.ascii_letters + string.digits + "_"
+        if isinstance(attr, str):
+            # By assuming most people are using string first we get 
substantial speed ups
+            if attr.isidentifier() and not iskeyword(attr):
+                return attr
 
         if isinstance(attr, tuple):
             attr = "_".join([str(x) for x in attr])
@@ -739,10 +777,18 @@
         if self.__box_config()["camel_killer_box"]:
             attr = _camel_killer(attr)
 
+        if attr.isidentifier() and not iskeyword(attr):
+            return attr
+
+        if sum(1 for character in attr if character.isidentifier() and not 
iskeyword(character)) == 0:
+            attr = f'{self.__box_config()["box_safe_prefix"]}{attr}'
+            if attr.isidentifier() and not iskeyword(attr):
+                return attr
+
         out = []
         last_safe = 0
         for i, character in enumerate(attr):
-            if character in allowed:
+            if f"x{character}".isidentifier():
                 last_safe = i
                 out.append(character)
             elif not out:
@@ -760,7 +806,7 @@
         else:
             out = f'{self.__box_config()["box_safe_prefix"]}{out}'
 
-        if out in kwlist:
+        if iskeyword(out):
             out = f'{self.__box_config()["box_safe_prefix"]}{out}'
 
         return out
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/box/box.pyi new/Box-6.0.2/box/box.pyi
--- old/Box-5.4.1/box/box.pyi   2021-08-22 17:06:26.000000000 +0200
+++ new/Box-6.0.2/box/box.pyi   2022-04-02 04:24:21.000000000 +0200
@@ -9,6 +9,7 @@
         default_box: bool = ...,
         default_box_attr: Any = ...,
         default_box_none_transform: bool = ...,
+        default_box_create_on_get: bool = ...,
         frozen_box: bool = ...,
         camel_killer_box: bool = ...,
         conversion_box: bool = ...,
@@ -27,6 +28,7 @@
         default_box: bool = ...,
         default_box_attr: Any = ...,
         default_box_none_transform: bool = ...,
+        default_box_create_on_get: bool = ...,
         frozen_box: bool = ...,
         camel_killer_box: bool = ...,
         conversion_box: bool = ...,
@@ -54,8 +56,8 @@
     def copy(self) -> Box: ...
     def __copy__(self) -> Box: ...
     def __deepcopy__(self, memodict: Any = ...) -> Box: ...
-    def __getitem__(self, item: Any, _ignore_default: bool = ...): ...
-    def __getattr__(self, item: Any): ...
+    def __getitem__(self, item: Any, _ignore_default: bool = ...) -> Any: ...
+    def __getattr__(self, item: Any) -> Any: ...
     def __setitem__(self, key: Any, value: Any): ...
     def __setattr__(self, key: Any, value: Any): ...
     def __delitem__(self, key: Any): ...
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/box/box_list.py 
new/Box-6.0.2/box/box_list.py
--- old/Box-5.4.1/box/box_list.py       2021-08-22 17:06:26.000000000 +0200
+++ new/Box-6.0.2/box/box_list.py       2022-04-02 04:24:21.000000000 +0200
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 #
-# Copyright (c) 2017-2020 - Chris Griffith - MIT License
+# Copyright (c) 2017-2022 - Chris Griffith - MIT License
 import copy
 import re
 from os import PathLike
@@ -133,7 +133,7 @@
         return keys
 
     def __repr__(self):
-        return f"<BoxList: {self.to_list()}>"
+        return f"BoxList({self.to_list()})"
 
     def __str__(self):
         return str(self.to_list())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/box/config_box.py 
new/Box-6.0.2/box/config_box.py
--- old/Box-5.4.1/box/config_box.py     2021-08-22 17:06:26.000000000 +0200
+++ new/Box-6.0.2/box/config_box.py     2022-04-02 04:24:21.000000000 +0200
@@ -123,7 +123,7 @@
         return self.float(item, default)
 
     def __repr__(self):
-        return "<ConfigBox: {0}>".format(str(self.to_dict()))
+        return "ConfigBox({0})".format(str(self.to_dict()))
 
     def copy(self):
         return ConfigBox(super().copy())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/box/converters.py 
new/Box-6.0.2/box/converters.py
--- old/Box-5.4.1/box/converters.py     2021-08-22 17:06:26.000000000 +0200
+++ new/Box-6.0.2/box/converters.py     2022-04-02 04:24:21.000000000 +0200
@@ -8,22 +8,28 @@
 from io import StringIO
 from os import PathLike
 from pathlib import Path
-from typing import Union
+from typing import Union, Optional, Dict
 
 from box.exceptions import BoxError
 
-yaml_available = True
+pyyaml_available = True
+ruamel_available = True
 toml_available = True
 msgpack_available = True
 
 try:
-    import ruamel.yaml as yaml
+    from ruamel.yaml import version_info, YAML
 except ImportError:
-    try:
-        import yaml  # type: ignore
-    except ImportError:
-        yaml = None  # type: ignore
-        yaml_available = False
+    ruamel_available = False
+else:
+    if version_info[1] < 17:
+        ruamel_available = False
+
+try:
+    import yaml
+except ImportError:
+    pyyaml_available = False
+
 try:
     import toml
 except ImportError:
@@ -36,6 +42,8 @@
     msgpack = None  # type: ignore
     msgpack_available = False
 
+yaml_available = pyyaml_available or ruamel_available
+
 BOX_PARAMETERS = (
     "default_box",
     "default_box_attr",
@@ -44,7 +52,6 @@
     "camel_killer_box",
     "box_safe_prefix",
     "box_duplicates",
-    "ordered_box",
     "default_box_none_transform",
     "box_dots",
     "modify_tuples_box",
@@ -111,14 +118,39 @@
     default_flow_style: bool = False,
     encoding: str = "utf-8",
     errors: str = "strict",
+    ruamel_typ: str = "rt",
+    ruamel_attrs: Optional[Dict] = None,
     **yaml_kwargs,
 ):
+    if not ruamel_attrs:
+        ruamel_attrs = {}
     if filename:
         _exists(filename, create=True)
         with open(filename, "w", encoding=encoding, errors=errors) as f:
-            yaml.dump(obj, stream=f, default_flow_style=default_flow_style, 
**yaml_kwargs)
+            if ruamel_available:
+                yaml_dumper = YAML(typ=ruamel_typ)
+                yaml_dumper.default_flow_style = default_flow_style
+                for attr, value in ruamel_attrs.items():
+                    setattr(yaml_dumper, attr, value)
+                return yaml_dumper.dump(obj, stream=f, **yaml_kwargs)
+            elif pyyaml_available:
+                return yaml.dump(obj, stream=f, 
default_flow_style=default_flow_style, **yaml_kwargs)
+            else:
+                raise BoxError("No YAML Parser available, please install 
ruamel.yaml>0.17 or PyYAML")
+
     else:
-        return yaml.dump(obj, default_flow_style=default_flow_style, 
**yaml_kwargs)
+        if ruamel_available:
+            yaml_dumper = YAML(typ=ruamel_typ)
+            yaml_dumper.default_flow_style = default_flow_style
+            for attr, value in ruamel_attrs.items():
+                setattr(yaml_dumper, attr, value)
+            with StringIO() as string_stream:
+                yaml_dumper.dump(obj, stream=string_stream, **yaml_kwargs)
+                return string_stream.getvalue()
+        elif pyyaml_available:
+            return yaml.dump(obj, default_flow_style=default_flow_style, 
**yaml_kwargs)
+        else:
+            raise BoxError("No YAML Parser available, please install 
ruamel.yaml>0.17 or PyYAML")
 
 
 def _from_yaml(
@@ -126,16 +158,38 @@
     filename: Union[str, PathLike] = None,
     encoding: str = "utf-8",
     errors: str = "strict",
+    ruamel_typ: str = "rt",
+    ruamel_attrs: Optional[Dict] = None,
     **kwargs,
 ):
-    if "Loader" not in kwargs:
-        kwargs["Loader"] = yaml.SafeLoader
+    if not ruamel_attrs:
+        ruamel_attrs = {}
     if filename:
         _exists(filename)
         with open(filename, "r", encoding=encoding, errors=errors) as f:
-            data = yaml.load(f, **kwargs)
+            if ruamel_available:
+                yaml_loader = YAML(typ=ruamel_typ)
+                for attr, value in ruamel_attrs.items():
+                    setattr(yaml_loader, attr, value)
+                data = yaml_loader.load(stream=f)
+            elif pyyaml_available:
+                if "Loader" not in kwargs:
+                    kwargs["Loader"] = yaml.SafeLoader
+                data = yaml.load(f, **kwargs)
+            else:
+                raise BoxError("No YAML Parser available, please install 
ruamel.yaml>0.15 or PyYAML")
     elif yaml_string:
-        data = yaml.load(yaml_string, **kwargs)
+        if ruamel_available:
+            yaml_loader = YAML(typ=ruamel_typ)
+            for attr, value in ruamel_attrs.items():
+                setattr(yaml_loader, attr, value)
+            data = yaml_loader.load(stream=yaml_string)
+        elif pyyaml_available:
+            if "Loader" not in kwargs:
+                kwargs["Loader"] = yaml.SafeLoader
+            data = yaml.load(yaml_string, **kwargs)
+        else:
+            raise BoxError("No YAML Parser available, please install 
ruamel.yaml>0.17 or PyYAML")
     else:
         raise BoxError("from_yaml requires a string or filename")
     return data
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/box/converters.pyi 
new/Box-6.0.2/box/converters.pyi
--- old/Box-5.4.1/box/converters.pyi    2021-08-22 17:06:26.000000000 +0200
+++ new/Box-6.0.2/box/converters.pyi    2022-04-02 04:24:21.000000000 +0200
@@ -1,6 +1,6 @@
 from box.exceptions import BoxError as BoxError
 from os import PathLike as PathLike
-from typing import Any, Union
+from typing import Any, Union, Optional, Dict
 
 yaml_available: bool
 toml_available: bool
@@ -25,6 +25,8 @@
     default_flow_style: bool = False,
     encoding: str = "utf-8",
     errors: str = "strict",
+    ruamel_typ: str = "rt",
+    ruamel_attrs: Optional[Dict] = None,
     **yaml_kwargs,
 ) -> Any: ...
 def _from_yaml(
@@ -32,6 +34,8 @@
     filename: Union[str, PathLike] = None,
     encoding: str = "utf-8",
     errors: str = "strict",
+    ruamel_typ: str = "rt",
+    ruamel_attrs: Optional[Dict] = None,
     **kwargs,
 ) -> Any: ...
 def _to_toml(obj, filename: Union[str, PathLike] = None, encoding: str = 
"utf-8", errors: str = "strict") -> Any: ...
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/box/shorthand_box.py 
new/Box-6.0.2/box/shorthand_box.py
--- old/Box-5.4.1/box/shorthand_box.py  2021-08-22 17:06:26.000000000 +0200
+++ new/Box-6.0.2/box/shorthand_box.py  2022-04-02 04:24:21.000000000 +0200
@@ -41,7 +41,7 @@
         return self.to_toml()
 
     def __repr__(self):
-        return "<ShorthandBox: {0}>".format(str(self.to_dict()))
+        return "ShorthandBox({0})".format(str(self.to_dict()))
 
     def copy(self):
         return SBox(super(SBox, self).copy())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/requirements-dev.txt 
new/Box-6.0.2/requirements-dev.txt
--- old/Box-5.4.1/requirements-dev.txt  2021-08-22 17:06:26.000000000 +0200
+++ new/Box-6.0.2/requirements-dev.txt  2022-04-02 04:24:21.000000000 +0200
@@ -1,4 +1,5 @@
 # Files needed for pre-commit hooks
 black>=21.7b0
+Cython>=0.29
 mypy>=0.910
-pre-commit>=4.0.1
+pre-commit>=2.15
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/requirements-test.txt 
new/Box-6.0.2/requirements-test.txt
--- old/Box-5.4.1/requirements-test.txt 2021-08-22 17:06:26.000000000 +0200
+++ new/Box-6.0.2/requirements-test.txt 2022-04-02 04:24:21.000000000 +0200
@@ -2,7 +2,8 @@
 msgpack>=1.0
 pytest>=5.4.1
 pytest-cov>=2.8.1
-ruamel.yaml>=0.16,<0.17
+ruamel.yaml>=0.17
 toml>=0.10.2
+types-PyYAML>=6.0.3
 types-toml>=0.1.3
 wheel>=0.34.2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/requirements.txt 
new/Box-6.0.2/requirements.txt
--- old/Box-5.4.1/requirements.txt      2021-08-22 17:06:26.000000000 +0200
+++ new/Box-6.0.2/requirements.txt      2022-04-02 04:24:21.000000000 +0200
@@ -1,3 +1,3 @@
 msgpack>=1.0.0
-ruamel.yaml>=0.16.10,<0.17
+ruamel.yaml>=0.17
 toml>=0.10.2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/setup.py new/Box-6.0.2/setup.py
--- old/Box-5.4.1/setup.py      2021-08-22 17:06:26.000000000 +0200
+++ new/Box-6.0.2/setup.py      2022-04-02 04:24:21.000000000 +0200
@@ -5,11 +5,23 @@
 import multiprocessing  # noqa: F401
 import os
 import re
+from pathlib import Path
+import sys
 
 from setuptools import setup
 
 root = os.path.abspath(os.path.dirname(__file__))
 
+try:
+    from Cython.Build import cythonize
+except ImportError:
+    extra = None
+else:
+    extra = cythonize(
+        [str(file.relative_to(root)) for file in Path(root, 
"box").glob("*.py") if file.name != "__init__.py"],
+        compiler_directives={"language_level": 3},
+    )
+
 with open(os.path.join(root, "box", "__init__.py"), "r") as init_file:
     init_content = init_file.read()
 
@@ -31,6 +43,7 @@
     long_description_content_type="text/x-rst",
     py_modules=["box"],
     packages=["box"],
+    ext_modules=extra,
     python_requires=">=3.6",
     include_package_data=True,
     platforms="any",
@@ -41,6 +54,7 @@
         "Programming Language :: Python :: 3.7",
         "Programming Language :: Python :: 3.8",
         "Programming Language :: Python :: 3.9",
+        "Programming Language :: Python :: 3.10",
         "Programming Language :: Python :: Implementation :: CPython",
         "Development Status :: 5 - Production/Stable",
         "Natural Language :: English",
@@ -52,11 +66,14 @@
         "Topic :: Software Development :: Libraries :: Python Modules",
     ],
     extras_require={
-        "all": ["ruamel.yaml", "toml", "msgpack"],
-        "yaml": ["ruamel.yaml"],
-        "ruamel.yaml": ["ruamel.yaml"],
+        "all": ["ruamel.yaml>=0.17", "toml", "msgpack"],
+        "yaml": ["ruamel.yaml>=0.17"],
+        "ruamel.yaml": ["ruamel.yaml>=0.17"],
         "PyYAML": ["PyYAML"],
         "toml": ["toml"],
         "msgpack": ["msgpack"],
     },
 )
+
+if not extra:
+    print("WARNING: Cython not installed, could not optimize box.", 
file=sys.stderr)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/test/common.py new/Box-6.0.2/test/common.py
--- old/Box-5.4.1/test/common.py        2021-08-22 17:06:26.000000000 +0200
+++ new/Box-6.0.2/test/common.py        2022-04-02 04:24:21.000000000 +0200
@@ -83,7 +83,7 @@
     [1, 2, 3],
     {},
     ([], {}),
-    lambda x: x ** 2,
+    lambda x: x**2,
     function_example,
     ClassExample(),
 )  # type: ignore
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/test/test_box.py 
new/Box-6.0.2/test/test_box.py
--- old/Box-5.4.1/test/test_box.py      2021-08-22 17:06:26.000000000 +0200
+++ new/Box-6.0.2/test/test_box.py      2022-04-02 04:24:21.000000000 +0200
@@ -9,6 +9,7 @@
 import shutil
 from multiprocessing import Queue
 from pathlib import Path
+from io import StringIO
 from test.common import (
     data_json_file,
     data_yaml_file,
@@ -23,10 +24,11 @@
 )
 
 import pytest
-import ruamel.yaml as yaml
+from ruamel.yaml import YAML
 
-from box import Box, BoxError, BoxKeyError, BoxList, ConfigBox, SBox, box
-from box.box import _get_dot_paths  # type: ignore
+from box import Box, BoxError, BoxKeyError, BoxList, ConfigBox, SBox
+from box.box import _get_dot_paths, _camel_killer, _recursive_tuples  # type: 
ignore
+from box.converters import BOX_PARAMETERS
 
 
 def mp_queue_test(q):
@@ -58,8 +60,8 @@
         assert Box()._safe_attr(356) == "x356"
 
     def test_camel_killer(self):
-        assert box._camel_killer("CamelCase") == "camel_case"
-        assert box._camel_killer("Terrible321KeyA") == "terrible321_key_a"
+        assert _camel_killer("CamelCase") == "camel_case"
+        assert _camel_killer("Terrible321KeyA") == "terrible321_key_a"
         bx = Box(camel_killer_box=True, conversion_box=False)
 
         bx.DeadCamel = 3
@@ -88,7 +90,7 @@
         assert len(bx1.keys()) == 0
 
     def test_recursive_tuples(self):
-        out = box._recursive_tuples(
+        out = _recursive_tuples(
             ({"test": "a"}, ({"second": "b"}, {"third": "c"}, ("fourth",))), 
dict, recreate_tuples=True
         )
         assert isinstance(out, tuple)
@@ -108,7 +110,7 @@
         assert "TEST_KEY" not in bx.to_dict(), bx.to_dict()
         assert isinstance(bx["Key 2"].Key4, Box)
         assert "'key1': 'value1'" in str(bx)
-        assert repr(bx).startswith("<Box:")
+        assert repr(bx).startswith("Box(")
         bx2 = Box([((3, 4), "A"), ("_box_config", "test")])
         assert bx2[(3, 4)] == "A"
         assert bx2["_box_config"] == "test"
@@ -208,13 +210,15 @@
 
     def test_to_yaml_basic(self):
         a = Box(test_dict)
-        assert yaml.load(a.to_yaml(), Loader=yaml.SafeLoader) == test_dict
+        yaml = YAML(typ="safe")
+        assert yaml.load(a.to_yaml()) == test_dict
 
     def test_to_yaml_file(self):
         a = Box(test_dict)
         a.to_yaml(tmp_yaml_file)
         with open(tmp_yaml_file) as f:
-            data = yaml.load(f, Loader=yaml.SafeLoader)
+            yaml = YAML(typ="safe")
+            data = yaml.load(f)
             assert data == test_dict
 
     def test_dir(self):
@@ -347,7 +351,11 @@
         assert bx.key1 == "value1"
 
     def test_from_yaml(self):
-        bx = Box.from_yaml(yaml.dump(test_dict), conversion_box=False, 
default_box=True)
+        yaml = YAML(typ="safe")
+        with StringIO() as sio:
+            yaml.dump(test_dict, sio)
+            data = sio.getvalue()
+        bx = Box.from_yaml(data, conversion_box=False, default_box=True)
         assert isinstance(bx, Box)
         assert bx.key1 == "value1"
         assert bx.Key_2 == Box()
@@ -752,6 +760,13 @@
         assert isinstance(bx.get("b", {}), Box)
         assert "a" in bx.get("a", Box(a=1, conversion_box=False))
         assert isinstance(bx.get("a", [1, 2]), BoxList)
+        bx_dot = Box(a=Box(b=Box(c="me!")), box_dots=True)
+        assert bx_dot.get("a.b.c") == "me!"
+
+    def test_contains(self):
+        bx_dot = Box(a=Box(b=Box(c=Box())), box_dots=True)
+        assert "a.b.c" in bx_dot
+        assert "a.b.c.d" not in bx_dot
 
     def test_get_default_box(self):
         bx = Box(default_box=True)
@@ -917,6 +932,16 @@
 
             _parse_box_dots({}, "-")
 
+        with pytest.raises(KeyError):
+            b["a.b"]
+        with pytest.raises(BoxKeyError):
+            b["a.b"]
+
+        with pytest.raises(KeyError):
+            del b["a.b"]
+        with pytest.raises(BoxKeyError):
+            del b["a.b"]
+
     def test_unicode(self):
         bx = Box()
         bx["\U0001f631"] = 4
@@ -1278,6 +1303,55 @@
         assert box["b.c.d"].e == 2
         assert box.c.e == "test"
         assert isinstance(box["d.e.f"], Box)
-        assert box.d.e["f.g"] == True
+        assert box.d.e["f.g"] is True
         assert isinstance(box["e.f"], BoxList)
         assert box.e.f[1] == 2
+
+    def test_box_slice(self):
+        data = Box(qwe=123, asd=234, q=1)
+        assert data[:-1] == Box(qwe=123, asd=234)
+
+    def test_box_kwargs_should_not_be_included(self):
+        params = {
+            "default_box": True,
+            "default_box_attr": True,
+            "conversion_box": True,
+            "frozen_box": True,
+            "camel_killer_box": True,
+            "box_safe_prefix": "x",
+            "box_duplicates": "error",
+            "default_box_none_transform": True,
+            "box_dots": True,
+            "modify_tuples_box": True,
+            "box_intact_types": (),
+            "box_recast": True,
+        }
+
+        bx = Box(**params)
+        assert bx == Box()
+
+        for param in BOX_PARAMETERS:
+            assert param in params
+
+    def test_box_greek(self):
+        # WARNING ?? is ord 956 whereas ?? is ord 181 and will not work due to 
python NFKC normalization
+        a = Box()
+        a.??eq = 1
+        a.??eq = 2
+        assert a == Box({"??eq": 1, "??eq": 2})
+
+    def test_box_default_not_create_on_get(self):
+        box = Box(default_box=True)
+
+        assert box.a.b.c == Box()
+
+        assert box == Box(a=Box(b=Box(c=Box())))
+        assert "c" in box.a.b
+
+        box2 = Box(default_box=True, default_box_create_on_get=False)
+
+        assert box2.a.b.c == Box()
+
+        assert "c" not in box2.a.b
+
+        assert box2 == Box()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/test/test_box_list.py 
new/Box-6.0.2/test/test_box_list.py
--- old/Box-5.4.1/test/test_box_list.py 2021-08-22 17:06:26.000000000 +0200
+++ new/Box-6.0.2/test/test_box_list.py 2022-04-02 04:24:21.000000000 +0200
@@ -6,10 +6,11 @@
 import os
 import shutil
 from pathlib import Path
+from io import StringIO
 from test.common import test_root, tmp_dir
 
 import pytest
-import ruamel.yaml as yaml
+from ruamel.yaml import YAML
 import toml
 
 from box import Box, BoxError, BoxList
@@ -32,7 +33,7 @@
         assert new_list[-1].item == 22
         new_list.append([{"bad_item": 33}])
         assert new_list[-1][0].bad_item == 33
-        assert repr(new_list).startswith("<BoxList:")
+        assert repr(new_list).startswith("BoxList(")
         for x in new_list.to_list():
             assert not isinstance(x, (BoxList, Box))
         new_list.insert(0, {"test": 5})
@@ -83,17 +84,20 @@
 
     def test_box_list_to_yaml(self):
         bl = BoxList([{"item": 1, "CamelBad": 2}])
-        assert yaml.load(bl.to_yaml(), Loader=yaml.SafeLoader)[0]["item"] == 1
+        yaml = YAML()
+        assert yaml.load(bl.to_yaml())[0]["item"] == 1
 
     def test_box_list_from_yaml(self):
         alist = [{"item": 1}, {"CamelBad": 2}]
-        yaml_list = yaml.dump(alist)
-        bl = BoxList.from_yaml(yaml_list, camel_killer_box=True)
+        yaml = YAML()
+        with StringIO() as sio:
+            yaml.dump(alist, stream=sio)
+            bl = BoxList.from_yaml(sio.getvalue(), camel_killer_box=True)
         assert bl[0].item == 1
         assert bl[1].camel_bad == 2
 
         with pytest.raises(BoxError):
-            BoxList.from_yaml(yaml.dump({"a": 2}))
+            BoxList.from_yaml("a: 2")
 
     def test_box_list_to_toml(self):
         bl = BoxList([{"item": 1, "CamelBad": 2}])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/test/test_config_box.py 
new/Box-6.0.2/test/test_config_box.py
--- old/Box-5.4.1/test/test_config_box.py       2021-08-22 17:06:26.000000000 
+0200
+++ new/Box-6.0.2/test/test_config_box.py       2022-04-02 04:24:21.000000000 
+0200
@@ -38,7 +38,7 @@
         assert cns.bb.getfloat("Wooo", 4.4) == 4.4
         assert cns.bb.getboolean("huh", True) is True
         assert cns.bb.list("Waaaa", [1]) == [1]
-        assert repr(cns).startswith("<ConfigBox")
+        assert repr(cns).startswith("ConfigBox(")
 
     def test_dir(self):
         b = ConfigBox(test_dict)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/test/test_converters.py 
new/Box-6.0.2/test/test_converters.py
--- old/Box-5.4.1/test/test_converters.py       2021-08-22 17:06:26.000000000 
+0200
+++ new/Box-6.0.2/test/test_converters.py       2022-04-02 04:24:21.000000000 
+0200
@@ -8,7 +8,7 @@
 
 import msgpack
 import pytest
-import ruamel.yaml as yaml
+from ruamel.yaml import YAML
 
 from box import BoxError
 from box.converters import _from_toml, _to_json, _to_msgpack, _to_toml, 
_to_yaml
@@ -81,7 +81,8 @@
         assert "Rick Moranis" in movie_string
         _to_yaml(movie_data, filename=m_file)
         assert "Rick Moranis" in open(m_file).read()
-        assert yaml.load(open(m_file), Loader=yaml.SafeLoader) == 
yaml.load(movie_string, Loader=yaml.SafeLoader)
+        yaml = YAML()
+        assert yaml.load(open(m_file)) == yaml.load(movie_string)
 
     def test_to_msgpack(self):
         m_file = os.path.join(tmp_dir, "movie_data")
@@ -90,3 +91,14 @@
         _to_msgpack(movie_data, filename=m_file)
         assert b"Rick Moranis" in open(m_file, "rb").read()
         assert msgpack.unpack(open(m_file, "rb")) == msgpack.unpackb(msg_data)
+
+    def test_to_yaml_ruamel(self):
+        movie_string = _to_yaml(movie_data, ruamel_attrs={"width": 12})
+        multiline_except = """    - name: Roger
+        Rees
+      imdb: nm0715953
+      role: Sheriff
+        of Rottingham
+    - name: Amy
+        Yasbeck"""
+        assert multiline_except in movie_string
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Box-5.4.1/test/test_sbox.py 
new/Box-6.0.2/test/test_sbox.py
--- old/Box-5.4.1/test/test_sbox.py     2021-08-22 17:06:26.000000000 +0200
+++ new/Box-6.0.2/test/test_sbox.py     2022-04-02 04:24:21.000000000 +0200
@@ -3,7 +3,7 @@
 import json
 from test.common import test_dict
 
-import ruamel.yaml as yaml
+from ruamel.yaml import YAML
 
 from box import Box, SBox
 
@@ -17,9 +17,10 @@
         assert isinstance(pbox.inner, SBox)
         assert pbox.inner.camel_case == "Item"
         assert json.loads(pbox.json)["inner"]["camel_case"] == "Item"
-        test_item = yaml.load(pbox.yaml, Loader=yaml.SafeLoader)
+        yaml = YAML()
+        test_item = yaml.load(pbox.yaml)
         assert test_item["inner"]["camel_case"] == "Item"
-        assert repr(pbox["inner"]).startswith("<ShorthandBox")
+        assert repr(pbox["inner"]).startswith("ShorthandBox(")
         assert not isinstance(pbox.dict, Box)
         assert pbox.dict["inner"]["camel_case"] == "Item"
         assert pbox.toml.startswith('key1 = "value1"')

Reply via email to