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 2021-12-09 19:45:42
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-python-box (Old)
and /work/SRC/openSUSE:Factory/.python-python-box.new.2520 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-python-box"
Thu Dec 9 19:45:42 2021 rev:6 rq:937740 version:5.4.1
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-python-box/python-python-box.changes
2020-08-06 10:42:38.138135529 +0200
+++
/work/SRC/openSUSE:Factory/.python-python-box.new.2520/python-python-box.changes
2021-12-09 19:46:21.449154866 +0100
@@ -1,0 +2,35 @@
+Wed Dec 8 09:55:04 UTC 2021 - [email protected]
+
+- version update to 5.4.1
+ Version 5.4.1
+ -------------
+ * Fixing #205 setdefault behavior with box_dots (thanks to Ivan Pepelnjak)
+ Version 5.4.0
+ -------------
+ * Adding py.typed for mypy support (thanks to Dominic)
+ * Adding testing for Python 3.10-dev
+ * Fixing #189 by adding mappings for mypy
+ * Fixing setdefault behavior with box_dots (thanks to ipcoder)
+ * Changing #193 how magic methods are handled with default_box (thanks to
Rexbard)
+ Version 5.3.0
+ -------------
+ * Adding support for functions to box_recast (thanks to Jacob Hayes)
+ * Adding #181 support for extending or adding new items to list during
`merge_update` (thanks to Marcos Dione)
+ * Fixing maintain stacktrace cause for BoxKeyError and BoxValueError (thanks
to Jacob Hayes)
+ * Fixing #177 that emtpy yaml files raised errors instead of returning empty
objects (thanks to Tim Schwenke)
+ * Fixing #171 that `popitems` wasn't first checking if box was frozen
(thanks to Varun Madiath)
+ * Changing all files to LF line endings
+ * Removing duplicate `box_recast` calls (thanks to Jacob Hayes)
+ * Removing coveralls code coverage, due to repeated issues with service
+ Version 5.2.0
+ -------------
+ * Adding checks for frozen boxes to `pop`, `popitem` and `clear` (thanks to
Varun Madiath)
+ * Fixing requirements-test.txt (thanks to Fabian Affolter)
+ * Fixing Flake8 conflicts with black (thanks to Varun Madiath)
+ * Fixing coveralls update (thanks to Varun Madiath)
+ Version 5.1.1
+ -------------
+ * Adding testing for Python 3.9
+ * Fixing #165 `box_dots` to work with `default_box`
+
+-------------------------------------------------------------------
Old:
----
5.1.0.tar.gz
New:
----
5.4.1.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-python-box.spec ++++++
--- /var/tmp/diff_new_pack.OVRo8N/_old 2021-12-09 19:46:22.121155189 +0100
+++ /var/tmp/diff_new_pack.OVRo8N/_new 2021-12-09 19:46:22.125155191 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-python-box
#
-# Copyright (c) 2020 SUSE LLC
+# Copyright (c) 2021 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -20,14 +20,13 @@
# python_requires='>=3.6'
%define skip_python2 1
Name: python-python-box
-Version: 5.1.0
+Version: 5.4.1
Release: 0
Summary: Advanced Python dictionaries with dot notation access
License: MIT
Group: Development/Languages/Python
URL: https://github.com/cdgriffith/Box
Source: https://github.com/cdgriffith/Box/archive/%{version}.tar.gz
-BuildRequires: %{python_module pytest-runner}
BuildRequires: %{python_module setuptools}
BuildRequires: fdupes
BuildRequires: python-rpm-macros
++++++ 5.1.0.tar.gz -> 5.4.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/.github/workflows/pythonpublish.yml
new/Box-5.4.1/.github/workflows/pythonpublish.yml
--- old/Box-5.1.0/.github/workflows/pythonpublish.yml 2020-07-23
23:07:29.000000000 +0200
+++ new/Box-5.4.1/.github/workflows/pythonpublish.yml 2021-08-22
17:06:26.000000000 +0200
@@ -1,4 +1,4 @@
-# This workflows will upload a Python Package using Twine when a release is
created
+# This workflow will upload a Python Package using Twine when a release is
created
# For more information see:
https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
name: Upload Python Package
@@ -17,11 +17,11 @@
- name: Set up Python
uses: actions/setup-python@v1
with:
- python-version: '3.8'
+ python-version: '3.9'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- pip install setuptools wheel twine
+ pip install setuptools wheel twine --upgrade
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/.github/workflows/tests.yml
new/Box-5.4.1/.github/workflows/tests.yml
--- old/Box-5.1.0/.github/workflows/tests.yml 2020-07-23 23:07:29.000000000
+0200
+++ new/Box-5.4.1/.github/workflows/tests.yml 2021-08-22 17:06:26.000000000
+0200
@@ -5,16 +5,16 @@
on:
push:
- branches: [ master, development ]
+ branches: [ master, development, develop, test, tests ]
pull_request:
- branches: [ master, development ]
+ branches: [ master, development, develop, test, tests ]
jobs:
package_checks:
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: [3.8]
+ python-version: [3.9]
steps:
- uses: actions/checkout@v2
@@ -33,7 +33,7 @@
# stop the build if there are Python syntax errors, undefined names
or print statements
flake8 box --count --select=E9,F63,F7,F82,T001,T002,T003,T004
--show-source --statistics
# exit-zero treats all errors as warnings.
- flake8 . --count --exit-zero --max-complexity=20
--max-line-length=120 --statistics
+ 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
@@ -45,12 +45,12 @@
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: [3.6, 3.7, 3.8, pypy3]
+ python-version: [3.6, 3.7, 3.8, 3.9, 3.10-dev, pypy3]
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 }}
- name: Install dependencies
@@ -58,11 +58,8 @@
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-test.txt
- pip install coveralls
- name: Test with pytest
env:
- COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
pytest --cov=box test/
- coveralls
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/.pre-commit-config.yaml
new/Box-5.4.1/.pre-commit-config.yaml
--- old/Box-5.1.0/.pre-commit-config.yaml 2020-07-23 23:07:29.000000000
+0200
+++ new/Box-5.4.1/.pre-commit-config.yaml 2021-08-22 17:06:26.000000000
+0200
@@ -1,21 +1,39 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v2.5.0
+ rev: v4.0.1
hooks:
- - id: mixed-line-ending
- - id: trailing-whitespace
+ # Identify invalid files
+ - id: check-ast
+ - id: check-yaml
+ - id: check-json
+ - id: check-toml
+ # git checks
+ - id: check-merge-conflict
+ - id: check-added-large-files
+ exclude: ^test/data/.+
+ - id: detect-private-key
+ - id: check-case-conflict
+ # Python checks
+ - id: check-docstring-first
+ - id: debug-statements
- id: requirements-txt-fixer
- id: fix-encoding-pragma
- - id: check-byte-order-marker
- - id: debug-statements
- - id: check-yaml
+ - id: fix-byte-order-marker
+ # General quality checks
+ - id: mixed-line-ending
+ args: [--fix=lf]
+ - id: trailing-whitespace
+ args: [--markdown-linebreak-ext=md]
+ - id: check-executables-have-shebangs
+ - id: end-of-file-fixer
+ exclude: ^test/data/.+
- repo: https://github.com/ambv/black
- rev: 19.10b0
+ rev: 21.7b0
hooks:
- id: black
args: [--config=.black.toml]
- repo: https://github.com/pre-commit/mirrors-mypy
- rev: 'v0.770'
+ rev: 'v0.910'
hooks:
- id: mypy
additional_dependencies: [ruamel.yaml,toml,msgpack]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/AUTHORS.rst new/Box-5.4.1/AUTHORS.rst
--- old/Box-5.1.0/AUTHORS.rst 2020-07-23 23:07:29.000000000 +0200
+++ new/Box-5.4.1/AUTHORS.rst 2021-08-22 17:06:26.000000000 +0200
@@ -22,6 +22,11 @@
- Jonas Irgens Kylling (jkylling)
- Bruno Rocha (rochacbruno)
- Noam Graetz (NoamGraetz2)
+- Fabian Affolter (fabaff)
+- Varun Madiath (vamega)
+- Jacob Hayes (JacobHayes)
+- Dominic (Yobmod)
+- Ivan Pepelnjak (ipspace)
Suggestions and bug reporting:
@@ -69,3 +74,7 @@
- David Aronchick (aronchick)
- Alexander Kapustin (dyens)
- Marcelo Huerta (richieadler)
+- Tim Schwenke (trallnag)
+- Marcos Dione (mdione-cloudian)
+- Varun Madiath (vamega)
+- Rexbard
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/CHANGES.rst new/Box-5.4.1/CHANGES.rst
--- old/Box-5.1.0/CHANGES.rst 2020-07-23 23:07:29.000000000 +0200
+++ new/Box-5.4.1/CHANGES.rst 2021-08-22 17:06:26.000000000 +0200
@@ -1,35 +1,75 @@
Changelog
=========
+Version 5.4.1
+-------------
+
+* Fixing #205 setdefault behavior with box_dots (thanks to Ivan Pepelnjak)
+
+Version 5.4.0
+-------------
+
+* Adding py.typed for mypy support (thanks to Dominic)
+* Adding testing for Python 3.10-dev
+* Fixing #189 by adding mappings for mypy
+* Fixing setdefault behavior with box_dots (thanks to ipcoder)
+* Changing #193 how magic methods are handled with default_box (thanks to
Rexbard)
+
+
+Version 5.3.0
+-------------
+
+* Adding support for functions to box_recast (thanks to Jacob Hayes)
+* Adding #181 support for extending or adding new items to list during
`merge_update` (thanks to Marcos Dione)
+* Fixing maintain stacktrace cause for BoxKeyError and BoxValueError (thanks
to Jacob Hayes)
+* Fixing #177 that emtpy yaml files raised errors instead of returning empty
objects (thanks to Tim Schwenke)
+* Fixing #171 that `popitems` wasn't first checking if box was frozen (thanks
to Varun Madiath)
+* Changing all files to LF line endings
+* Removing duplicate `box_recast` calls (thanks to Jacob Hayes)
+* Removing coveralls code coverage, due to repeated issues with service
+
+Version 5.2.0
+-------------
+
+* Adding checks for frozen boxes to `pop`, `popitem` and `clear` (thanks to
Varun Madiath)
+* Fixing requirements-test.txt (thanks to Fabian Affolter)
+* Fixing Flake8 conflicts with black (thanks to Varun Madiath)
+* Fixing coveralls update (thanks to Varun Madiath)
+
+Version 5.1.1
+-------------
+
+* Adding testing for Python 3.9
+* Fixing #165 `box_dots` to work with `default_box`
Version 5.1.0
-------------
-* Adding `dotted` option for `items` function (thanks to ipcoder)
-* Fixing bug in box.set_default where value is dictionary, return the internal
value and not detached temporary (thanks to Noam Graetz)
+* Adding #152 `dotted` option for `items` function (thanks to ipcoder)
+* Fixing #157 bug in box.set_default where value is dictionary, return the
internal value and not detached temporary (thanks to Noam Graetz)
* Removing warnings on import if optional libraries are missing
Version 5.0.1
-------------
-* Fixing default box saving internal method calls and restricted options
(thanks to Marcelo Huerta)
+* Fixing #155 default box saving internal method calls and restricted options
(thanks to Marcelo Huerta)
Version 5.0.0
-------------
* Adding support for msgpack converters `to_msgpack` and `from_msgpack`
-* Adding support for comparision of `Box` to other boxes or dicts via the `-`
sub operator #144 (thanks to Hitz)
+* Adding #144 support for comparision of `Box` to other boxes or dicts via the
`-` sub operator (thanks to Hitz)
* Adding support to `|` union boxes like will come default in Python 3.9 from
PEP 0584
* Adding `mypy` type checking, `black` formatting and other checks on commit
-* Adding new parameter `box_class` for cleaner inheritance #148 (thanks to
David Aronchick)
-* Adding `dotted` option for `keys` method to return box_dots style keys
(thanks to ipcoder)
+* Adding #148 new parameter `box_class` for cleaner inheritance (thanks to
David Aronchick)
+* Adding #152 `dotted` option for `keys` method to return box_dots style keys
(thanks to ipcoder)
* Fixing box_dots to properly delete items from lists
* Fixing box_dots to properly find items with dots in their key
* Fixing that recast of subclassses of `Box` or `BoxList` were not fed box
properties (thanks to Alexander Kapustin)
-* Changing that sub boxes are always created to properly propagate settings
and copy objects #150 (thanks to ipcoder)
-* Changing that default_box will not raise key errors on `pop` #67 (thanks to
Patrock)
+* Changing #150 that sub boxes are always created to properly propagate
settings and copy objects (thanks to ipcoder)
+* Changing #67 that default_box will not raise key errors on `pop` (thanks to
Patrock)
* Changing `to_csv` and `from_csv` to have same string and filename options as
all other transforms
-* Changing back to no required external imports, instead have extra requires
like [all] (thanks to wim glenn)
+* Changing #127 back to no required external imports, instead have extra
requires like [all] (thanks to wim glenn)
* Changing from putting all details in README.rst to a github wiki at
https://github.com/cdgriffith/Box/wiki
* Changing `BoxList.box_class` to be stored in `BoxList.box_options` dict as
`box_class`
* Changing `del` will raise `BoxKeyError`, subclass of both `KeyError` and
`BoxError`
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/MANIFEST.in new/Box-5.4.1/MANIFEST.in
--- old/Box-5.1.0/MANIFEST.in 2020-07-23 23:07:29.000000000 +0200
+++ new/Box-5.4.1/MANIFEST.in 2021-08-22 17:06:26.000000000 +0200
@@ -1,3 +1,4 @@
include LICENSE
include AUTHORS.rst
include CHANGES.rst
+include box/py.typed
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/README.rst new/Box-5.4.1/README.rst
--- old/Box-5.1.0/README.rst 2020-07-23 23:07:29.000000000 +0200
+++ new/Box-5.4.1/README.rst 2021-08-22 17:06:26.000000000 +0200
@@ -1,4 +1,4 @@
-|BuildStatus| |CoverageStatus| |License|
+|BuildStatus| |License|
|BoxImage|
@@ -103,8 +103,6 @@
:target: https://github.com/cdgriffith/Box
.. |BuildStatus| image::
https://github.com/cdgriffith/Box/workflows/Tests/badge.svg?branch=master
:target: https://github.com/cdgriffith/Box/actions?query=workflow%3ATests
-.. |CoverageStatus| image::
https://img.shields.io/coveralls/cdgriffith/Box/master.svg?maxAge=2592000
- :target: https://coveralls.io/r/cdgriffith/Box?branch=master
.. |License| image:: https://img.shields.io/pypi/l/python-box.svg
:target: https://pypi.python.org/pypi/python-box/
@@ -113,4 +111,4 @@
.. _`Wrapt Documentation`: https://wrapt.readthedocs.io/en/latest
.. _reusables: https://github.com/cdgriffith/reusables#reusables
.. _created:
https://github.com/cdgriffith/Reusables/commit/df20de4db74371c2fedf1578096f3e29c93ccdf3#diff-e9a0f470ef3e8afb4384dc2824943048R51
-.. _LICENSE: https://github.com/cdgriffith/Box/blob/master/LICENSE
\ No newline at end of file
+.. _LICENSE: https://github.com/cdgriffith/Box/blob/master/LICENSE
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/box/__init__.py
new/Box-5.4.1/box/__init__.py
--- old/Box-5.1.0/box/__init__.py 2020-07-23 23:07:29.000000000 +0200
+++ new/Box-5.4.1/box/__init__.py 2021-08-22 17:06:26.000000000 +0200
@@ -2,11 +2,21 @@
# -*- coding: utf-8 -*-
__author__ = "Chris Griffith"
-__version__ = "5.1.0"
+__version__ = "5.4.1"
from box.box import Box
from box.box_list import BoxList
from box.config_box import ConfigBox
-from box.shorthand_box import SBox
from box.exceptions import BoxError, BoxKeyError
from box.from_file import box_from_file
+from box.shorthand_box import SBox
+
+__all__ = [
+ "Box",
+ "BoxList",
+ "ConfigBox",
+ "BoxError",
+ "BoxKeyError",
+ "box_from_file",
+ "SBox",
+]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/box/box.py new/Box-5.4.1/box/box.py
--- old/Box-5.1.0/box/box.py 2020-07-23 23:07:29.000000000 +0200
+++ new/Box-5.4.1/box/box.py 2021-08-22 17:06:26.000000000 +0200
@@ -9,26 +9,29 @@
import re
import string
import warnings
-from os import PathLike
-from collections.abc import Iterable, Mapping, Callable
from keyword import kwlist
-from typing import Any, Union, Tuple, List, Dict, Generator
+from os import PathLike
+from typing import Any, Dict, Generator, List, Tuple, Union
+try:
+ from typing import Callable, Iterable, Mapping
+except ImportError:
+ from collections.abc import Callable, Iterable, Mapping
import box
from box.converters import (
- _to_json,
+ BOX_PARAMETERS,
_from_json,
+ _from_msgpack,
_from_toml,
- _to_toml,
_from_yaml,
- _to_yaml,
+ _to_json,
_to_msgpack,
- _from_msgpack,
- BOX_PARAMETERS,
- yaml_available,
- toml_available,
+ _to_toml,
+ _to_yaml,
msgpack_available,
+ toml_available,
+ yaml_available,
)
from box.exceptions import BoxError, BoxKeyError, BoxTypeError, BoxValueError,
BoxWarning
@@ -43,6 +46,16 @@
NO_DEFAULT = object()
+def _exception_cause(e):
+ """
+ Unwrap BoxKeyError and BoxValueError errors to their cause.
+
+ Use with `raise ... from _exception_cause(err)` to avoid deeply nested
stacktraces, but keep the
+ context.
+ """
+ return e.__cause__ if isinstance(e, (BoxKeyError, BoxValueError)) else e
+
+
def _camel_killer(attr):
"""
CamelKiller, qu'est-ce que c'est?
@@ -80,6 +93,28 @@
raise BoxError("Could not split box dots properly")
+def _get_dot_paths(bx, current=""):
+ """A generator of all the end node keys in a box in box_dots format"""
+
+ def handle_dicts(sub_bx, paths=""):
+ for key, value in sub_bx.items():
+ yield f"{paths}.{key}" if paths else key
+ if isinstance(value, dict):
+ yield from handle_dicts(value, f"{paths}.{key}" if paths else
key)
+ elif isinstance(value, list):
+ yield from handle_lists(value, f"{paths}.{key}" if paths else
key)
+
+ def handle_lists(bx_list, paths=""):
+ for i, value in enumerate(bx_list):
+ yield f"{paths}[{i}]"
+ if isinstance(value, list):
+ yield from handle_lists(value, f"{paths}[{i}]")
+ if isinstance(value, dict):
+ yield from handle_dicts(value, f"{paths}[{i}]")
+
+ yield from handle_dicts(bx, current)
+
+
def _get_box_config():
return {
# Internal use only
@@ -228,47 +263,47 @@
self._box_config["__created"] = True
- def __add__(self, other: dict):
+ def __add__(self, other: Mapping[Any, Any]):
if not isinstance(other, dict):
- raise BoxTypeError(f"Box can only merge two boxes or a box and a
dictionary.")
+ raise BoxTypeError("Box can only merge two boxes or a box and a
dictionary.")
new_box = self.copy()
new_box.merge_update(other)
return new_box
- def __radd__(self, other: dict):
+ def __radd__(self, other: Mapping[Any, Any]):
if not isinstance(other, dict):
- raise BoxTypeError(f"Box can only merge two boxes or a box and a
dictionary.")
+ raise BoxTypeError("Box can only merge two boxes or a box and a
dictionary.")
new_box = self.copy()
new_box.merge_update(other)
return new_box
- def __iadd__(self, other: dict):
+ def __iadd__(self, other: Mapping[Any, Any]):
if not isinstance(other, dict):
- raise BoxTypeError(f"Box can only merge two boxes or a box and a
dictionary.")
+ raise BoxTypeError("Box can only merge two boxes or a box and a
dictionary.")
self.merge_update(other)
return self
- def __or__(self, other: dict):
+ def __or__(self, other: Mapping[Any, Any]):
if not isinstance(other, dict):
- raise BoxTypeError(f"Box can only merge two boxes or a box and a
dictionary.")
+ raise BoxTypeError("Box can only merge two boxes or a box and a
dictionary.")
new_box = self.copy()
new_box.update(other)
return new_box
- def __ror__(self, other: dict):
+ def __ror__(self, other: Mapping[Any, Any]):
if not isinstance(other, dict):
- raise BoxTypeError(f"Box can only merge two boxes or a box and a
dictionary.")
+ raise BoxTypeError("Box can only merge two boxes or a box and a
dictionary.")
new_box = self.copy()
new_box.update(other)
return new_box
- def __ior__(self, other: dict):
+ def __ior__(self, other: Mapping[Any, Any]):
if not isinstance(other, dict):
- raise BoxTypeError(f"Box can only merge two boxes or a box and a
dictionary.")
+ raise BoxTypeError("Box can only merge two boxes or a box and a
dictionary.")
self.update(other)
return self
- def __sub__(self, other: dict):
+ def __sub__(self, other: Mapping[Any, Any]):
frozen = self._box_config["frozen_box"]
config = self.__box_config()
config["frozen_box"] = False
@@ -399,7 +434,7 @@
value = default_value.copy()
else:
value = default_value
- if not attr or (not item.startswith("_") and not item.endswith("_")):
+ if not attr or not (item.startswith("_") and item.endswith("_")):
super().__setitem__(item, value)
return value
@@ -412,13 +447,14 @@
def __recast(self, item, value):
if self._box_config["box_recast"] and item in
self._box_config["box_recast"]:
+ recast = self._box_config["box_recast"][item]
try:
- if issubclass(self._box_config["box_recast"][item], (Box,
box.BoxList)):
- return self._box_config["box_recast"][item](value,
**self.__box_config())
+ if isinstance(recast, type) and issubclass(recast, (Box,
box.BoxList)):
+ return recast(value, **self.__box_config())
else:
- return self._box_config["box_recast"][item](value)
- except ValueError:
- raise BoxValueError(f'Cannot convert {value} to
{self._box_config["box_recast"][item]}') from None
+ return recast(value)
+ except ValueError as err:
+ raise BoxValueError(f"Cannot convert {value} to {recast}")
from _exception_cause(err)
return value
def __convert_and_store(self, item, value):
@@ -452,9 +488,15 @@
return super().__getitem__(item)
except KeyError as err:
if item == "_box_config":
- raise BoxKeyError("_box_config should only exist as an
attribute and is never defaulted") from None
+ cause = _exception_cause(err)
+ raise BoxKeyError("_box_config should only exist as an
attribute and is never defaulted") from cause
if self._box_config["box_dots"] and isinstance(item, str) and ("."
in item or "[" in item):
- first_item, children = _parse_box_dots(self, item)
+ try:
+ first_item, children = _parse_box_dots(self, item)
+ except BoxError:
+ if self._box_config["default_box"] and not _ignore_default:
+ return self.__get_default(item)
+ raise
if first_item in self.keys():
if hasattr(self[first_item], "__getitem__"):
return self[first_item][children]
@@ -464,7 +506,7 @@
return super().__getitem__(converted)
if self._box_config["default_box"] and not _ignore_default:
return self.__get_default(item)
- raise BoxKeyError(str(err)) from None
+ raise BoxKeyError(str(err)) from _exception_cause(err)
def __getattr__(self, item):
try:
@@ -474,20 +516,22 @@
value = object.__getattribute__(self, item)
except AttributeError as err:
if item == "__getstate__":
- raise BoxKeyError(item) from None
+ raise BoxKeyError(item) from _exception_cause(err)
if item == "_box_config":
- raise BoxError("_box_config key must exist") from None
+ raise BoxError("_box_config key must exist") from
_exception_cause(err)
if self._box_config["conversion_box"]:
safe_key = self._safe_attr(item)
if safe_key in self._box_config["__safe_keys"]:
return
self.__getitem__(self._box_config["__safe_keys"][safe_key])
if self._box_config["default_box"]:
+ if item.startswith("_") and item.endswith("_"):
+ raise BoxKeyError(f"{item}: Does not exist and internal
methods are never defaulted")
return self.__get_default(item, attr=True)
- raise BoxKeyError(str(err)) from None
+ raise BoxKeyError(str(err)) from _exception_cause(err)
return value
def __setitem__(self, key, value):
- if key != "_box_config" and self._box_config["__created"] and
self._box_config["frozen_box"]:
+ if key != "_box_config" and self._box_config["frozen_box"] and
self._box_config["__created"]:
raise BoxError("Box is frozen")
if self._box_config["box_dots"] and isinstance(key, str) and ("." in
key or "[" in key):
first_item, children = _parse_box_dots(self, key, setting=True)
@@ -509,7 +553,6 @@
raise BoxKeyError(f'Key name "{key}" is protected')
if key == "_box_config":
return object.__setattr__(self, key, value)
- value = self.__recast(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]
@@ -536,7 +579,7 @@
try:
super().__delitem__(key)
except KeyError as err:
- raise BoxKeyError(str(err)) from None
+ raise BoxKeyError(str(err)) from _exception_cause(err)
def __delattr__(self, item):
if self._box_config["frozen_box"]:
@@ -554,9 +597,12 @@
self.__delitem__(self._box_config["__safe_keys"][safe_key])
del self._box_config["__safe_keys"][safe_key]
return
- raise BoxKeyError(str(err)) from None
+ raise BoxKeyError(str(err)) from _exception_cause(err)
def pop(self, key, *args):
+ if self._box_config["frozen_box"]:
+ raise BoxError("Box is frozen")
+
if args:
if len(args) != 1:
raise BoxError('pop() takes only one optional argument
"default"')
@@ -576,10 +622,14 @@
return item
def clear(self):
+ if self._box_config["frozen_box"]:
+ raise BoxError("Box is frozen")
super().clear()
self._box_config["__safe_keys"].clear()
def popitem(self):
+ if self._box_config["frozen_box"]:
+ raise BoxError("Box is frozen")
try:
key = next(self.__iter__())
except StopIteration:
@@ -617,6 +667,9 @@
return out_dict
def update(self, __m=None, **kwargs):
+ if self._box_config["frozen_box"]:
+ raise BoxError("Box is frozen")
+
if __m:
if hasattr(__m, "keys"):
for k in __m:
@@ -639,6 +692,15 @@
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
+ if merge_type == "unique" and k in self and
isinstance(self[k], list):
+ for item in v:
+ if item not in self[k]:
+ self[k].append(item)
+ return
self.__setitem__(k, v)
if __m:
@@ -655,6 +717,10 @@
if item in self:
return self[item]
+ if self._box_config["box_dots"]:
+ if item in _get_dot_paths(self):
+ return self[item]
+
if isinstance(default, dict):
default = self._box_config["box_class"](default,
**self.__box_config())
if isinstance(default, list):
@@ -813,6 +879,8 @@
box_args[arg] = kwargs.pop(arg)
data = _from_yaml(yaml_string=yaml_string, filename=filename,
encoding=encoding, errors=errors, **kwargs)
+ if not data:
+ return cls(**box_args)
if not isinstance(data, dict):
raise BoxError(f"yaml data not returned as a dictionary but
rather a {type(data).__name__}")
return cls(data, **box_args)
@@ -909,7 +977,12 @@
return _to_msgpack(self.to_dict(), filename=filename, **kwargs)
@classmethod
- def from_msgpack(cls, msgpack_bytes: bytes = None, filename:
Union[str, PathLike] = None, **kwargs,) -> "Box":
+ def from_msgpack(
+ cls,
+ msgpack_bytes: bytes = None,
+ filename: Union[str, PathLike] = None,
+ **kwargs,
+ ) -> "Box":
"""
Transforms msgpack bytes or file into a Box object
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/box/box.pyi new/Box-5.4.1/box/box.pyi
--- old/Box-5.1.0/box/box.pyi 1970-01-01 01:00:00.000000000 +0100
+++ new/Box-5.4.1/box/box.pyi 2021-08-22 17:06:26.000000000 +0200
@@ -0,0 +1,115 @@
+from collections.abc import Mapping
+from os import PathLike
+from typing import Any, Dict, Generator, List, Optional, Tuple, Union
+
+class Box(dict):
+ def __new__(
+ cls: Any,
+ *args: Any,
+ default_box: bool = ...,
+ default_box_attr: Any = ...,
+ default_box_none_transform: bool = ...,
+ frozen_box: bool = ...,
+ camel_killer_box: bool = ...,
+ conversion_box: bool = ...,
+ modify_tuples_box: bool = ...,
+ box_safe_prefix: str = ...,
+ box_duplicates: str = ...,
+ box_intact_types: Union[Tuple, List] = ...,
+ box_recast: Dict = ...,
+ box_dots: bool = ...,
+ box_class: Union[Dict, Box] = ...,
+ **kwargs: Any,
+ ) -> Any: ...
+ def __init__(
+ self,
+ *args: Any,
+ default_box: bool = ...,
+ default_box_attr: Any = ...,
+ default_box_none_transform: bool = ...,
+ frozen_box: bool = ...,
+ camel_killer_box: bool = ...,
+ conversion_box: bool = ...,
+ modify_tuples_box: bool = ...,
+ box_safe_prefix: str = ...,
+ box_duplicates: str = ...,
+ box_intact_types: Union[Tuple, List] = ...,
+ box_recast: Dict = ...,
+ box_dots: bool = ...,
+ box_class: Union[Dict, Box] = ...,
+ **kwargs: Any,
+ ) -> None: ...
+ def __add__(self, other: Mapping[Any, Any]) -> Any: ...
+ def __radd__(self, other: Mapping[Any, Any]) -> Any: ...
+ def __iadd__(self, other: Mapping[Any, Any]) -> Any: ...
+ def __or__(self, other: Mapping[Any, Any]) -> Any: ...
+ def __ror__(self, other: Mapping[Any, Any]) -> Any: ...
+ def __ior__(self, other: Mapping[Any, Any]) -> Any: ...
+ def __sub__(self, other: Mapping[Any, Any]) -> Any: ...
+ def __hash__(self) -> Any: ... # type: ignore[override]
+ def __dir__(self): ...
+ def keys(self, dotted: Union[bool] = ...) -> Any: ...
+ def items(self, dotted: Union[bool] = ...) -> Any: ...
+ def get(self, key: Any, default: Any = ...): ...
+ 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 __setitem__(self, key: Any, value: Any): ...
+ def __setattr__(self, key: Any, value: Any): ...
+ def __delitem__(self, key: Any): ...
+ def __delattr__(self, item: Any) -> None: ...
+ def pop(self, key: Any, *args: Any): ...
+ def clear(self) -> None: ...
+ def popitem(self): ...
+ def __iter__(self) -> Generator: ...
+ def __reversed__(self) -> Generator: ...
+ def to_dict(self) -> Dict: ...
+ def update(self, __m: Optional[Any] = ..., **kwargs: Any) -> None: ...
+ def merge_update(self, __m: Optional[Any] = ..., **kwargs: Any) -> None:
...
+ def setdefault(self, item: Any, default: Optional[Any] = ...): ...
+ def to_json(
+ self, filename: Union[str, PathLike] = ..., encoding: str = ...,
errors: str = ..., **json_kwargs: Any
+ ) -> Any: ...
+ @classmethod
+ def from_json(
+ cls: Any,
+ json_string: str = ...,
+ filename: Union[str, PathLike] = ...,
+ encoding: str = ...,
+ errors: str = ...,
+ **kwargs: Any,
+ ) -> Box: ...
+ def to_yaml(
+ self,
+ filename: Union[str, PathLike] = ...,
+ default_flow_style: bool = ...,
+ encoding: str = ...,
+ errors: str = ...,
+ **yaml_kwargs: Any,
+ ) -> Any: ...
+ @classmethod
+ def from_yaml(
+ cls: Any,
+ yaml_string: str = ...,
+ filename: Union[str, PathLike] = ...,
+ encoding: str = ...,
+ errors: str = ...,
+ **kwargs: Any,
+ ) -> Box: ...
+ def to_toml(self, filename: Union[str, PathLike] = ..., encoding: str =
..., errors: str = ...) -> Any: ...
+ @classmethod
+ def from_toml(
+ cls: Any,
+ toml_string: str = ...,
+ filename: Union[str, PathLike] = ...,
+ encoding: str = ...,
+ errors: str = ...,
+ **kwargs: Any,
+ ) -> Box: ...
+ def to_msgpack(self, filename: Union[str, PathLike] = ..., **kwargs: Any)
-> Any: ...
+ @classmethod
+ def from_msgpack(
+ cls: Any, msgpack_bytes: bytes = ..., filename: Union[str, PathLike] =
..., **kwargs: Any
+ ) -> Box: ...
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/box/box_list.py
new/Box-5.4.1/box/box_list.py
--- old/Box-5.1.0/box/box_list.py 2020-07-23 23:07:29.000000000 +0200
+++ new/Box-5.4.1/box/box_list.py 2021-08-22 17:06:26.000000000 +0200
@@ -6,24 +6,23 @@
import re
from os import PathLike
from typing import Iterable, Type, Union
-from pathlib import Path
import box
from box.converters import (
- _to_yaml,
- _from_yaml,
- _to_json,
+ BOX_PARAMETERS,
+ _from_csv,
_from_json,
- _to_toml,
+ _from_msgpack,
_from_toml,
+ _from_yaml,
_to_csv,
- _from_csv,
+ _to_json,
_to_msgpack,
- _from_msgpack,
- BOX_PARAMETERS,
- yaml_available,
- toml_available,
+ _to_toml,
+ _to_yaml,
msgpack_available,
+ toml_available,
+ yaml_available,
)
from box.exceptions import BoxError, BoxTypeError
@@ -217,10 +216,10 @@
:param kwargs: parameters to pass to `Box()` or `json.loads`
:return: BoxList object from json data
"""
- bx_args = {}
+ box_args = {}
for arg in list(kwargs.keys()):
if arg in BOX_PARAMETERS:
- bx_args[arg] = kwargs.pop(arg)
+ box_args[arg] = kwargs.pop(arg)
data = _from_json(
json_string, filename=filename, encoding=encoding, errors=errors,
multiline=multiline, **kwargs
@@ -228,7 +227,7 @@
if not isinstance(data, list):
raise BoxError(f"json data not returned as a list, but rather a
{type(data).__name__}")
- return cls(data, **bx_args)
+ return cls(data, **box_args)
if yaml_available:
@@ -278,15 +277,17 @@
:param kwargs: parameters to pass to `BoxList()` or `yaml.load`
:return: BoxList object from yaml data
"""
- bx_args = {}
+ box_args = {}
for arg in list(kwargs.keys()):
if arg in BOX_PARAMETERS:
- bx_args[arg] = kwargs.pop(arg)
+ box_args[arg] = kwargs.pop(arg)
data = _from_yaml(yaml_string=yaml_string, filename=filename,
encoding=encoding, errors=errors, **kwargs)
+ if not data:
+ return cls(**box_args)
if not isinstance(data, list):
raise BoxError(f"yaml data not returned as a list but rather a
{type(data).__name__}")
- return cls(data, **bx_args)
+ return cls(data, **box_args)
else:
@@ -354,15 +355,15 @@
:param kwargs: parameters to pass to `Box()`
:return:
"""
- bx_args = {}
+ box_args = {}
for arg in list(kwargs.keys()):
if arg in BOX_PARAMETERS:
- bx_args[arg] = kwargs.pop(arg)
+ box_args[arg] = kwargs.pop(arg)
data = _from_toml(toml_string=toml_string, filename=filename,
encoding=encoding, errors=errors)
if key_name not in data:
raise BoxError(f"{key_name} was not found.")
- return cls(data[key_name], **bx_args)
+ return cls(data[key_name], **box_args)
else:
@@ -408,15 +409,15 @@
:param kwargs: parameters to pass to `Box()`
:return:
"""
- bx_args = {}
+ box_args = {}
for arg in list(kwargs.keys()):
if arg in BOX_PARAMETERS:
- bx_args[arg] = kwargs.pop(arg)
+ box_args[arg] = kwargs.pop(arg)
data = _from_msgpack(msgpack_bytes=msgpack_bytes,
filename=filename, **kwargs)
if not isinstance(data, list):
raise BoxError(f"msgpack data not returned as a list but
rather a {type(data).__name__}")
- return cls(data, **bx_args)
+ return cls(data, **box_args)
else:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/box/box_list.pyi
new/Box-5.4.1/box/box_list.pyi
--- old/Box-5.1.0/box/box_list.pyi 1970-01-01 01:00:00.000000000 +0100
+++ new/Box-5.4.1/box/box_list.pyi 2021-08-22 17:06:26.000000000 +0200
@@ -0,0 +1,85 @@
+import box
+from box.converters import (
+ BOX_PARAMETERS as BOX_PARAMETERS,
+ msgpack_available as msgpack_available,
+ toml_available as toml_available,
+ yaml_available as yaml_available,
+)
+from box.exceptions import BoxError as BoxError, BoxTypeError as BoxTypeError
+from os import PathLike as PathLike
+from typing import Any, Iterable, Optional, Type, Union
+
+class BoxList(list):
+ def __new__(cls, *args: Any, **kwargs: Any): ...
+ box_options: Any = ...
+ box_org_ref: Any = ...
+ def __init__(self, iterable: Iterable = ..., box_class: Type[box.Box] =
..., **box_options: Any) -> None: ...
+ def __getitem__(self, item: Any): ...
+ def __delitem__(self, key: Any): ...
+ def __setitem__(self, key: Any, value: Any): ...
+ def append(self, p_object: Any) -> None: ...
+ def extend(self, iterable: Any) -> None: ...
+ def insert(self, index: Any, p_object: Any) -> None: ...
+ def __copy__(self): ...
+ def __deepcopy__(self, memo: Optional[Any] = ...): ...
+ def __hash__(self) -> Any: ... # type: ignore[override]
+ def to_list(self): ...
+ def to_json(
+ self,
+ filename: Union[str, PathLike] = ...,
+ encoding: str = ...,
+ errors: str = ...,
+ multiline: bool = ...,
+ **json_kwargs: Any,
+ ) -> Any: ...
+ @classmethod
+ def from_json(
+ cls: Any,
+ json_string: str = ...,
+ filename: Union[str, PathLike] = ...,
+ encoding: str = ...,
+ errors: str = ...,
+ multiline: bool = ...,
+ **kwargs: Any,
+ ) -> Any: ...
+ def to_yaml(
+ self,
+ filename: Union[str, PathLike] = ...,
+ default_flow_style: bool = ...,
+ encoding: str = ...,
+ errors: str = ...,
+ **yaml_kwargs: Any,
+ ) -> Any: ...
+ @classmethod
+ def from_yaml(
+ cls: Any,
+ yaml_string: str = ...,
+ filename: Union[str, PathLike] = ...,
+ encoding: str = ...,
+ errors: str = ...,
+ **kwargs: Any,
+ ) -> Any: ...
+ def to_toml(
+ self, filename: Union[str, PathLike] = ..., key_name: str = ...,
encoding: str = ..., errors: str = ...
+ ) -> Any: ...
+ @classmethod
+ def from_toml(
+ cls: Any,
+ toml_string: str = ...,
+ filename: Union[str, PathLike] = ...,
+ key_name: str = ...,
+ encoding: str = ...,
+ errors: str = ...,
+ **kwargs: Any,
+ ) -> Any: ...
+ def to_msgpack(self, filename: Union[str, PathLike] = ..., **kwargs: Any)
-> Any: ...
+ @classmethod
+ def from_msgpack(
+ cls: Any, msgpack_bytes: bytes = ..., filename: Union[str, PathLike] =
..., **kwargs: Any
+ ) -> Any: ...
+ def to_csv(self, filename: Union[str, PathLike] = ..., encoding: str =
..., errors: str = ...) -> Any: ...
+ @classmethod
+ def from_csv(
+ cls: Any, csv_string: str = ..., filename: Union[str, PathLike] = ...,
encoding: str = ..., errors: str = ...
+ ) -> Any: ...
+ def _dotted_helper(self) -> Any: ...
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/box/config_box.py
new/Box-5.4.1/box/config_box.py
--- old/Box-5.1.0/box/config_box.py 2020-07-23 23:07:29.000000000 +0200
+++ new/Box-5.4.1/box/config_box.py 2021-08-22 17:06:26.000000000 +0200
@@ -87,7 +87,7 @@
raise err
return float(item)
- def list(self, item, default=None, spliter=",", strip=True, mod=None):
+ def list(self, item, default=None, spliter: str = ",", strip=True,
mod=None):
"""
Return value of key as a list
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/box/config_box.pyi
new/Box-5.4.1/box/config_box.pyi
--- old/Box-5.1.0/box/config_box.pyi 1970-01-01 01:00:00.000000000 +0100
+++ new/Box-5.4.1/box/config_box.pyi 2021-08-22 17:06:26.000000000 +0200
@@ -0,0 +1,15 @@
+from box.box import Box as Box
+from typing import Any, Optional
+
+class ConfigBox(Box):
+ def __getattr__(self, item: Any): ...
+ def __dir__(self): ...
+ def bool(self, item: Any, default: Optional[Any] = ...): ...
+ def int(self, item: Any, default: Optional[Any] = ...): ...
+ def float(self, item: Any, default: Optional[Any] = ...): ...
+ def list(self, item: Any, default: Optional[Any] = ..., spliter: str =
..., strip: bool = ..., mod: Optional[Any] = ...): ... # type: ignore
+ def getboolean(self, item: Any, default: Optional[Any] = ...): ...
+ def getint(self, item: Any, default: Optional[Any] = ...): ...
+ def getfloat(self, item: Any, default: Optional[Any] = ...): ...
+ def copy(self): ...
+ def __copy__(self): ...
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/box/converters.py
new/Box-5.4.1/box/converters.py
--- old/Box-5.1.0/box/converters.py 2020-07-23 23:07:29.000000000 +0200
+++ new/Box-5.4.1/box/converters.py 2021-08-22 17:06:26.000000000 +0200
@@ -5,9 +5,9 @@
import csv
import json
-from pathlib import Path
from io import StringIO
from os import PathLike
+from pathlib import Path
from typing import Union
from box.exceptions import BoxError
@@ -24,12 +24,12 @@
except ImportError:
yaml = None # type: ignore
yaml_available = False
-
try:
import toml
except ImportError:
toml = None # type: ignore
toml_available = False
+
try:
import msgpack # type: ignore
except ImportError:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/box/converters.pyi
new/Box-5.4.1/box/converters.pyi
--- old/Box-5.1.0/box/converters.pyi 1970-01-01 01:00:00.000000000 +0100
+++ new/Box-5.4.1/box/converters.pyi 2021-08-22 17:06:26.000000000 +0200
@@ -0,0 +1,52 @@
+from box.exceptions import BoxError as BoxError
+from os import PathLike as PathLike
+from typing import Any, Union
+
+yaml_available: bool
+toml_available: bool
+msgpack_available: bool
+BOX_PARAMETERS: Any
+
+def _exists(filename: Union[str, PathLike], create: bool = False) -> Any: ...
+def _to_json(
+ obj, filename: Union[str, PathLike] = None, encoding: str = "utf-8",
errors: str = "strict", **json_kwargs
+) -> Any: ...
+def _from_json(
+ json_string: str = None,
+ filename: Union[str, PathLike] = None,
+ encoding: str = "utf-8",
+ errors: str = "strict",
+ multiline: bool = False,
+ **kwargs,
+) -> Any: ...
+def _to_yaml(
+ obj,
+ filename: Union[str, PathLike] = None,
+ default_flow_style: bool = False,
+ encoding: str = "utf-8",
+ errors: str = "strict",
+ **yaml_kwargs,
+) -> Any: ...
+def _from_yaml(
+ yaml_string: str = None,
+ filename: Union[str, PathLike] = None,
+ encoding: str = "utf-8",
+ errors: str = "strict",
+ **kwargs,
+) -> Any: ...
+def _to_toml(obj, filename: Union[str, PathLike] = None, encoding: str =
"utf-8", errors: str = "strict") -> Any: ...
+def _from_toml(
+ toml_string: str = None, filename: Union[str, PathLike] = None, encoding:
str = "utf-8", errors: str = "strict"
+) -> Any: ...
+def _to_msgpack(obj, filename: Union[str, PathLike] = None, **kwargs) -> Any:
...
+def _from_msgpack(msgpack_bytes: bytes = None, filename: Union[str, PathLike]
= None, **kwargs) -> Any: ...
+def _to_csv(
+ box_list, filename: Union[str, PathLike] = None, encoding: str = "utf-8",
errors: str = "strict", **kwargs
+) -> Any: ...
+def _from_csv(
+ csv_string: str = None,
+ filename: Union[str, PathLike] = None,
+ encoding: str = "utf-8",
+ errors: str = "strict",
+ **kwargs,
+) -> Any: ...
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/box/exceptions.pyi
new/Box-5.4.1/box/exceptions.pyi
--- old/Box-5.1.0/box/exceptions.pyi 1970-01-01 01:00:00.000000000 +0100
+++ new/Box-5.4.1/box/exceptions.pyi 2021-08-22 17:06:26.000000000 +0200
@@ -0,0 +1,5 @@
+class BoxError(Exception): ...
+class BoxKeyError(BoxError, KeyError, AttributeError): ...
+class BoxTypeError(BoxError, TypeError): ...
+class BoxValueError(BoxError, ValueError): ...
+class BoxWarning(UserWarning): ...
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/box/from_file.py
new/Box-5.4.1/box/from_file.py
--- old/Box-5.1.0/box/from_file.py 2020-07-23 23:07:29.000000000 +0200
+++ new/Box-5.4.1/box/from_file.py 2021-08-22 17:06:26.000000000 +0200
@@ -1,9 +1,14 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from json import JSONDecodeError
-from pathlib import Path
from os import PathLike
-from typing import Union, Callable, Dict
+from pathlib import Path
+from typing import Callable, Dict, Union
+
+from box.box import Box
+from box.box_list import BoxList
+from box.converters import msgpack_available, toml_available, yaml_available
+from box.exceptions import BoxError
try:
from ruamel.yaml import YAMLError
@@ -14,7 +19,7 @@
YAMLError = False # type: ignore
try:
- from toml import TomlDecodeError
+ from toml import TomlDecodeError # type: ignore
except ImportError:
TomlDecodeError = False # type: ignore
@@ -23,10 +28,6 @@
except ImportError:
UnpackException = False # type: ignore
-from box.exceptions import BoxError
-from box.box import Box
-from box.box_list import BoxList
-from box.converters import yaml_available, toml_available, msgpack_available
__all__ = ["box_from_file"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/box/from_file.pyi
new/Box-5.4.1/box/from_file.pyi
--- old/Box-5.1.0/box/from_file.pyi 1970-01-01 01:00:00.000000000 +0100
+++ new/Box-5.4.1/box/from_file.pyi 2021-08-22 17:06:26.000000000 +0200
@@ -0,0 +1,8 @@
+from box.box import Box
+from box.box_list import BoxList
+from os import PathLike
+from typing import Any, Union
+
+def box_from_file(
+ file: Union[str, PathLike], file_type: str = ..., encoding: str = ...,
errors: str = ..., **kwargs: Any
+) -> Union[Box, BoxList]: ...
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/box/shorthand_box.pyi
new/Box-5.4.1/box/shorthand_box.pyi
--- old/Box-5.1.0/box/shorthand_box.pyi 1970-01-01 01:00:00.000000000 +0100
+++ new/Box-5.4.1/box/shorthand_box.pyi 2021-08-22 17:06:26.000000000 +0200
@@ -0,0 +1,13 @@
+from box.box import Box as Box
+
+class SBox(Box):
+ @property
+ def dict(self): ...
+ @property
+ def json(self): ...
+ @property
+ def yaml(self): ...
+ @property
+ def toml(self): ...
+ def copy(self): ...
+ def __copy__(self): ...
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/requirements-dev.txt
new/Box-5.4.1/requirements-dev.txt
--- old/Box-5.1.0/requirements-dev.txt 2020-07-23 23:07:29.000000000 +0200
+++ new/Box-5.4.1/requirements-dev.txt 2021-08-22 17:06:26.000000000 +0200
@@ -1,4 +1,4 @@
# Files needed for pre-commit hooks
-black>=19.10b0
-mypy>=0.770
-pre-commit>=2.2.0
+black>=21.7b0
+mypy>=0.910
+pre-commit>=4.0.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/requirements-test.txt
new/Box-5.4.1/requirements-test.txt
--- old/Box-5.1.0/requirements-test.txt 2020-07-23 23:07:29.000000000 +0200
+++ new/Box-5.4.1/requirements-test.txt 2021-08-22 17:06:26.000000000 +0200
@@ -1,5 +1,8 @@
coverage>=5.0.4
-pytest-cov>=2.8.1
+msgpack>=1.0
pytest>=5.4.1
-reusables>=0.9.5
+pytest-cov>=2.8.1
+ruamel.yaml>=0.16,<0.17
+toml>=0.10.2
+types-toml>=0.1.3
wheel>=0.34.2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/requirements.txt
new/Box-5.4.1/requirements.txt
--- old/Box-5.1.0/requirements.txt 2020-07-23 23:07:29.000000000 +0200
+++ new/Box-5.4.1/requirements.txt 2021-08-22 17:06:26.000000000 +0200
@@ -1,3 +1,3 @@
msgpack>=1.0.0
-ruamel.yaml>=0.16.10
-toml>=0.10.1
+ruamel.yaml>=0.16.10,<0.17
+toml>=0.10.2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/setup.py new/Box-5.4.1/setup.py
--- old/Box-5.1.0/setup.py 2020-07-23 23:07:29.000000000 +0200
+++ new/Box-5.4.1/setup.py 2021-08-22 17:06:26.000000000 +0200
@@ -1,12 +1,12 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-from setuptools import setup
+# Must import multiprocessing as a fix for issues with testing, experienced on
win10
+import multiprocessing # noqa: F401
import os
import re
-# Fix for issues with testing, experienced on win10
-import multiprocessing
+from setuptools import setup
root = os.path.abspath(os.path.dirname(__file__))
@@ -40,6 +40,7 @@
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: Implementation :: CPython",
"Development Status :: 5 - Production/Stable",
"Natural Language :: English",
@@ -52,6 +53,7 @@
],
extras_require={
"all": ["ruamel.yaml", "toml", "msgpack"],
+ "yaml": ["ruamel.yaml"],
"ruamel.yaml": ["ruamel.yaml"],
"PyYAML": ["PyYAML"],
"toml": ["toml"],
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/test/test_box.py
new/Box-5.4.1/test/test_box.py
--- old/Box-5.1.0/test/test_box.py 2020-07-23 23:07:29.000000000 +0200
+++ new/Box-5.4.1/test/test_box.py 2021-08-22 17:06:26.000000000 +0200
@@ -5,29 +5,29 @@
import json
import os
import pickle
+import platform
import shutil
from multiprocessing import Queue
from pathlib import Path
-import platform
-
-import pytest
-import ruamel.yaml as yaml
-
-from box import box
-from box import Box, BoxError, BoxKeyError, BoxList, SBox, ConfigBox
from test.common import (
- test_dict,
- extended_test_dict,
- tmp_dir,
- tmp_json_file,
- tmp_yaml_file,
- movie_data,
data_json_file,
data_yaml_file,
+ extended_test_dict,
+ movie_data,
+ test_dict,
test_root,
+ tmp_dir,
+ tmp_json_file,
tmp_msgpack_file,
+ tmp_yaml_file,
)
+import pytest
+import ruamel.yaml as yaml
+
+from box import Box, BoxError, BoxKeyError, BoxList, ConfigBox, SBox, box
+from box.box import _get_dot_paths # type: ignore
+
def mp_queue_test(q):
bx = q.get()
@@ -307,6 +307,30 @@
assert a.key3.item == 2
assert a.lister[0].gah == 7
+ def test_set_default_box_dots(self):
+ a = Box(box_dots=True)
+ a["x"] = {"y": 10}
+ a.setdefault("x.y", 20)
+ assert a["x.y"] == 10
+
+ a["lists"] = [[[{"test": "here"}], {1, 2}], (4, 5)]
+ assert list(_get_dot_paths(a)) == [
+ "x",
+ "x.y",
+ "lists",
+ "lists[0]",
+ "lists[0][0]",
+ "lists[0][0][0]",
+ "lists[0][0][0].test",
+ "lists[0][1]",
+ "lists[1]",
+ ]
+
+ t = Box({"a": 1}, default_box=True, box_dots=True,
default_box_none_transform=False)
+ assert t.setdefault("b", [1, 2]) == [1, 2]
+ assert t == Box(a=1, b=[1, 2])
+ assert t.setdefault("c", [{"d": 2}]) == BoxList([{"d": 2}])
+
def test_from_json_file(self):
bx = Box.from_json(filename=data_json_file)
assert isinstance(bx, Box)
@@ -329,17 +353,17 @@
assert bx.Key_2 == Box()
def test_bad_from_json(self):
- with pytest.raises(BoxError) as err:
+ with pytest.raises(BoxError):
Box.from_json()
- with pytest.raises(BoxError) as err2:
+ with pytest.raises(BoxError):
Box.from_json(json_string="[1]")
def test_bad_from_yaml(self):
- with pytest.raises(BoxError) as err:
+ with pytest.raises(BoxError):
Box.from_yaml()
- with pytest.raises(BoxError) as err2:
+ with pytest.raises(BoxError):
Box.from_yaml("lol")
def test_conversion_box(self):
@@ -371,6 +395,21 @@
with pytest.raises(TypeError):
hash(bx)
+ with pytest.raises(BoxError):
+ bx.clear()
+
+ with pytest.raises(BoxError):
+ bx.pop("alist")
+
+ with pytest.raises(BoxError):
+ bx.popitem()
+
+ with pytest.raises(BoxError):
+ bx.popitem()
+
+ with pytest.raises(BoxError):
+ bx.update({"another_list": []})
+
bx2 = Box(test_dict)
with pytest.raises(TypeError):
hash(bx2)
@@ -568,7 +607,7 @@
assert a[1].b == 3
def test_duplicate_errors(self):
- with pytest.raises(BoxError) as err:
+ with pytest.raises(BoxError):
Box({"?a": 1, "!a": 3}, box_duplicates="error")
Box({"?a": 1, "!a": 3}, box_duplicates="ignore")
@@ -756,7 +795,9 @@
def test_inheritance(self):
data = {
- "users": [{"users": [{"name": "B"}]},],
+ "users": [
+ {"users": [{"name": "B"}]},
+ ],
}
class UsersBoxList(BoxList):
@@ -974,6 +1015,21 @@
with pytest.raises(ValueError):
b["sub_box"] = {"id": "bad_id"}
+ def test_nontype_recast(self):
+ class CustomError(ValueError):
+ pass
+
+ def cast_id(val) -> int:
+ if val == "bad_id":
+ raise CustomError()
+ return int(val)
+
+ b = Box(id="6", box_recast={"id": cast_id})
+ assert isinstance(b.id, int)
+ with pytest.raises(ValueError) as exc_info:
+ b["sub_box"] = {"id": "bad_id"}
+ assert isinstance(exc_info.value.__cause__, CustomError)
+
def test_box_dots(self):
b = Box(
{"my_key": {"does stuff": {"to get to": "where I want"}},
"key.with.list": [[[{"test": "value"}]]]},
@@ -1137,5 +1193,91 @@
def test_default_box_restricted_calls(self):
a = Box(default_box=True)
- a._test_thing_
+ with pytest.raises(BoxKeyError):
+ a._test_thing_
assert len(list(a.keys())) == 0
+
+ # Based on argparse.parse_args internal behavior, the following
+ # creates the attribute in hasattr due to default_box=True, then
+ # deletes it in delattr.
+ if hasattr(a, "_unrecognized_args"):
+ delattr(a, "_unrecognized_args")
+
+ a._allowed_prefix
+ a.allowed_postfix_
+ assert len(list(a.keys())) == 2
+
+ def test_default_dots(self):
+ a = Box(default_box=True, box_dots=True)
+ a["a.a.a"]
+ assert a == {"a.a.a": {}}
+ a["a.a.a."]
+ a["a.a.a.."]
+ assert a == {"a.a.a": {"": {"": {}}}}
+ a["b.b"] = 3
+ assert a == {"a.a.a": {"": {"": {}}}, "b.b": 3}
+ a.b.b = 4
+ assert a == {"a.a.a": {"": {"": {}}}, "b.b": 3, "b": {"b": 4}}
+ assert a["non.existent.key"] == {}
+
+ def test_merge_list_options(self):
+ a = Box()
+ a.merge_update({"lister": ["a"]})
+ a.merge_update({"lister": ["a", "b", "c"]}, box_merge_lists="extend")
+ assert a.lister == ["a", "a", "b", "c"]
+ a.merge_update({"lister": ["a", "b", "c"]}, box_merge_lists="unique")
+ assert a.lister == ["a", "a", "b", "c"]
+ a.merge_update({"lister": ["a", "d", "b", "c"]},
box_merge_lists="unique")
+ assert a.lister == ["a", "a", "b", "c", "d"]
+ a.merge_update({"key1": {"new": 5}, "Key 2": {"add_key": 6}, "lister":
["a"]})
+ assert a.lister == ["a"]
+
+ def test_box_from_empty_yaml(self):
+ out = Box.from_yaml("---")
+ assert out == Box()
+
+ out2 = BoxList.from_yaml("---")
+ assert out2 == BoxList()
+
+ def test_setdefault_simple(self):
+ box = Box({"a": 1})
+ box.setdefault("b", 2)
+ box.setdefault("c", "test")
+ box.setdefault("d", {"e": True})
+ box.setdefault("f", [1, 2])
+
+ assert box["b"] == 2
+ assert box["c"] == "test"
+ assert isinstance(box["d"], Box)
+ assert box["d"]["e"] == True
+ assert isinstance(box["f"], BoxList)
+ assert box["f"][1] == 2
+
+ def test_setdefault_dots(self):
+ box = Box({"a": 1}, box_dots=True)
+ box.setdefault("b", 2)
+ box.c = {"d": 3}
+ box.setdefault("c.e", "test")
+ box.setdefault("d", {"e": True})
+ box.setdefault("f", [1, 2])
+
+ assert box.b == 2
+ assert box.c.e == "test"
+ assert isinstance(box["d"], Box)
+ assert box.d.e == True
+ assert isinstance(box["f"], BoxList)
+ assert box.f[1] == 2
+
+ def test_setdefault_dots_default(self):
+ box = Box({"a": 1}, box_dots=True, default_box=True)
+ box.b.c.d.setdefault("e", 2)
+ box.c.setdefault("e", "test")
+ box.d.e.setdefault("f", {"g": True})
+ box.e.setdefault("f", [1, 2])
+
+ 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 isinstance(box["e.f"], BoxList)
+ assert box.e.f[1] == 2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/test/test_box_list.py
new/Box-5.4.1/test/test_box_list.py
--- old/Box-5.1.0/test/test_box_list.py 2020-07-23 23:07:29.000000000 +0200
+++ new/Box-5.4.1/test/test_box_list.py 2021-08-22 17:06:26.000000000 +0200
@@ -6,14 +6,13 @@
import os
import shutil
from pathlib import Path
+from test.common import test_root, tmp_dir
import pytest
import ruamel.yaml as yaml
import toml
-from box import BoxList, Box, BoxError
-
-from test.common import tmp_dir, test_root
+from box import Box, BoxError, BoxList
class TestBoxList:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/test/test_config_box.py
new/Box-5.4.1/test/test_config_box.py
--- old/Box-5.1.0/test/test_config_box.py 2020-07-23 23:07:29.000000000
+0200
+++ new/Box-5.4.1/test/test_config_box.py 2021-08-22 17:06:26.000000000
+0200
@@ -1,9 +1,10 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-from box import Box, ConfigBox
from test.common import test_dict
+from box import Box, ConfigBox
+
class TestConfigBox:
def test_config_box(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/test/test_converters.py
new/Box-5.4.1/test/test_converters.py
--- old/Box-5.1.0/test/test_converters.py 2020-07-23 23:07:29.000000000
+0200
+++ new/Box-5.4.1/test/test_converters.py 2021-08-22 17:06:26.000000000
+0200
@@ -1,18 +1,17 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
+import json
import os
import shutil
from pathlib import Path
-import json
+from test.common import movie_data, tmp_dir
+import msgpack
import pytest
import ruamel.yaml as yaml
-import msgpack
from box import BoxError
-from box.converters import _to_toml, _from_toml, _to_json, _to_yaml,
_to_msgpack
-from test.common import tmp_dir, movie_data
-
+from box.converters import _from_toml, _to_json, _to_msgpack, _to_toml,
_to_yaml
toml_string = """[movies.Spaceballs]
imdb_stars = 7.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/test/test_from_file.py
new/Box-5.4.1/test/test_from_file.py
--- old/Box-5.1.0/test/test_from_file.py 2020-07-23 23:07:29.000000000
+0200
+++ new/Box-5.4.1/test/test_from_file.py 2021-08-22 17:06:26.000000000
+0200
@@ -1,11 +1,11 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pathlib import Path
+from test.common import test_root
import pytest
-from box import box_from_file, Box, BoxList, BoxError
-from test.common import test_root
+from box import Box, BoxError, BoxList, box_from_file
class TestFromFile:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Box-5.1.0/test/test_sbox.py
new/Box-5.4.1/test/test_sbox.py
--- old/Box-5.1.0/test/test_sbox.py 2020-07-23 23:07:29.000000000 +0200
+++ new/Box-5.4.1/test/test_sbox.py 2021-08-22 17:06:26.000000000 +0200
@@ -1,11 +1,11 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import json
+from test.common import test_dict
import ruamel.yaml as yaml
-from box import SBox, Box
-from test.common import test_dict
+from box import Box, SBox
class TestSBox: