From a23451d2869a511280eebe194efca41efadd2706 Mon Sep 17 00:00:00 2001 From: Chris Griffith Date: Wed, 12 Jun 2024 15:06:30 -0500 Subject: [PATCH] Version 7.2.0 (#271) * Adding #266 support for accessing nested items in BoxList using numpy-style tuple indexing (thanks to Bit0r) * Adding tests and Cython releases for Python 3.12 * Fixing #251 support for circular references in lists (thanks to Muspi Merol) * Fixing #261 altering all `__repr__` methods so that subclassing will output the correct class name (thanks to Gabriel Tkacz) * Fixing #267 Fix type 'int' not iterable (thanks to YISH) --------- Co-authored-by: Bit0r Co-authored-by: Muspi Merol Co-authored-by: Gabriel Tkacz <55806524+gtkacz@users.noreply.github.com> Co-authored-by: Gabriel Tkacz Co-authored-by: YISH --- .github/workflows/pythonpublish.yml | 23 +++++++++++----------- .github/workflows/tests.yml | 30 ++++++++++++++--------------- .pre-commit-config.yaml | 10 +++++----- AUTHORS.rst | 5 ++++- CHANGES.rst | 9 +++++++++ box/__init__.py | 2 +- box/box.py | 5 +++-- box/box_list.py | 21 ++++++++++++++++---- box/config_box.py | 2 +- box/shorthand_box.py | 4 ++-- test/test_box_list.py | 12 +++++++----- 11 files changed, 75 insertions(+), 48 deletions(-) diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index e24267a..791b92a 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -13,12 +13,12 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: '3.12' - name: Install Dependencies run: | @@ -39,13 +39,13 @@ jobs: strategy: matrix: os: [macos-11, windows-latest] - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -63,11 +63,11 @@ jobs: deploy-cython-manylinux: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.11 - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - name: Set up Python 3.12 + uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: "3.12" - name: Build wheels run: | @@ -75,7 +75,7 @@ jobs: pip install cibuildwheel setuptools wheel python -m cibuildwheel --output-dir dist env: - CIBW_BUILD: cp38-manylinux_x86_64 cp39-manylinux_x86_64 cp310-manylinux_x86_64 cp311-manylinux_x86_64 cp311-macosx_x86_64 + CIBW_BUILD: cp38-manylinux_x86_64 cp39-manylinux_x86_64 cp310-manylinux_x86_64 cp311-manylinux_x86_64 cp312-manylinux_x86_64 CIBW_BEFORE_BUILD: pip install Cython==3.0.0 CIBW_BEFORE_TEST: pip install -r requirements.txt -r requirements-test.txt setuptools wheel twine CIBW_TEST_COMMAND: pytest {package}/test -vv @@ -87,4 +87,3 @@ jobs: run: | pip install twine twine upload dist/*-manylinux*.whl - twine upload dist/*-macosx*.whl diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index db506f9..389b419 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,13 +13,13 @@ jobs: package-checks: strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12-dev", "pypy-3.8"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "pypy-3.8"] os: [ubuntu-latest, macos-11, windows-latest] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - uses: actions/cache@v3 @@ -58,19 +58,19 @@ jobs: Remove-item box -recurse -force python -m pytest -vv - name: Upload wheel artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: python_box + name: python_box_${{matrix.os}}_${{ matrix.python-version }} path: dist/*.whl package-manylinux-checks: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.11 - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - name: Set up Python 3.12 + uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: "3.12" - uses: actions/cache@v3 with: @@ -83,27 +83,27 @@ jobs: pip install cibuildwheel python -m cibuildwheel --output-dir dist env: - CIBW_BUILD: cp38-manylinux_x86_64 cp39-manylinux_x86_64 cp310-manylinux_x86_64 cp311-manylinux_x86_64 cp311-macosx_x86_64 + CIBW_BUILD: cp38-manylinux_x86_64 cp39-manylinux_x86_64 cp310-manylinux_x86_64 cp311-manylinux_x86_64 cp312-manylinux_x86_64 CIBW_BEFORE_BUILD: pip install Cython==3.0.0 CIBW_BEFORE_TEST: pip install -r requirements.txt -r requirements-test.txt setuptools wheel twine CIBW_TEST_COMMAND: pytest {package}/test -vv - name: Upload wheel artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: python_box + name: python_box_manylinux path: dist/*-manylinux*.whl test: strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] os: [ubuntu-latest, macos-11, windows-latest] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - uses: actions/cache@v3 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index eb3c1f0..b91d368 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.6.0 hooks: # Identify invalid files - id: check-ast @@ -20,8 +20,8 @@ repos: - id: fix-encoding-pragma - id: fix-byte-order-marker # General quality checks - - id: mixed-line-ending - args: [--fix=lf] +# - id: mixed-line-ending +# args: [--fix=lf] - id: trailing-whitespace args: [--markdown-linebreak-ext=md] - id: check-executables-have-shebangs @@ -29,7 +29,7 @@ repos: exclude: ^test/data/.+ - repo: https://github.com/ambv/black - rev: 23.7.0 + rev: 24.4.2 hooks: - id: black args: [--config=.black.toml] @@ -51,7 +51,7 @@ repos: always_run: true - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v1.4.1' + rev: 'v1.10.0' hooks: - id: mypy types: [python] diff --git a/AUTHORS.rst b/AUTHORS.rst index b67a735..c0b068d 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -30,7 +30,10 @@ Code contributions: - Michał Górny (mgorny) - Serge Lu (Serge45) - Eric Prestat (ericpre) - +- Gabriel Mitelman Tkacz (gtkacz) +- Muspi Merol (CNSeniorious000) +- YISH (mokeyish) +- Bit0r Suggestions and bug reporting: diff --git a/CHANGES.rst b/CHANGES.rst index 0272697..f2db4c8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,15 @@ Changelog ========= +Version 7.2.0 +------------- + +* Adding #266 support for accessing nested items in BoxList using numpy-style tuple indexing (thanks to Bit0r) +* Adding tests and Cython releases for Python 3.12 +* Fixing #251 support for circular references in lists (thanks to Muspi Merol) +* Fixing #261 altering all `__repr__` methods so that subclassing will output the correct class name (thanks to Gabriel Tkacz) +* Fixing #267 Fix type 'int' not iterable (thanks to YISH) + Version 7.1.1 ------------- diff --git a/box/__init__.py b/box/__init__.py index 10b4ce6..adc6199 100644 --- a/box/__init__.py +++ b/box/__init__.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- __author__ = "Chris Griffith" -__version__ = "7.1.1" +__version__ = "7.2.0" from box.box import Box from box.box_list import BoxList diff --git a/box/box.py b/box/box.py index 158a55e..6729616 100644 --- a/box/box.py +++ b/box/box.py @@ -415,7 +415,8 @@ def __contains__(self, item): except BoxError: return False else: - return children in self[first_item] + it = self[first_item] + return isinstance(it, Iterable) and children in it def keys(self, dotted: Union[bool] = False): if not dotted: @@ -777,7 +778,7 @@ def popitem(self): return key, self.pop(key) def __repr__(self) -> str: - return f"Box({self})" + return f"{self.__class__.__name__}({self})" def __str__(self) -> str: return str(self.to_dict()) diff --git a/box/box_list.py b/box/box_list.py index 048b014..3535f22 100644 --- a/box/box_list.py +++ b/box/box_list.py @@ -40,16 +40,17 @@ def __new__(cls, *args, **kwargs): # This is required for pickling to work correctly obj.box_options = {"box_class": box.Box} obj.box_options.update(kwargs) - obj.box_org_ref = 0 + obj.box_org_ref = None return obj def __init__(self, iterable: Optional[Iterable] = None, box_class: Type[box.Box] = box.Box, **box_options): self.box_options = box_options self.box_options["box_class"] = box_class - self.box_org_ref = id(iterable) if iterable else 0 + self.box_org_ref = iterable if iterable: for x in iterable: self.append(x) + self.box_org_ref = None if box_options.get("frozen_box"): def frozen(*args, **kwargs): @@ -65,6 +66,14 @@ def __getitem__(self, item): if len(list_pos.group()) == len(item): return value return value.__getitem__(item[len(list_pos.group()) :].lstrip(".")) + if isinstance(item, tuple): + result = self + for idx in item: + if isinstance(result, list): + result = result[idx] + else: + raise BoxTypeError(f"Cannot numpy-style indexing on {type(result).__name__}.") + return result return super().__getitem__(item) def __delitem__(self, key): @@ -101,7 +110,11 @@ def _convert(self, p_object): elif isinstance(p_object, box.Box): p_object._box_config.update(self.box_options) if isinstance(p_object, list) and not self._is_intact_type(p_object): - p_object = self.__class__(p_object, **self.box_options) + p_object = ( + self + if p_object is self or p_object is self.box_org_ref + else self.__class__(p_object, **self.box_options) + ) elif isinstance(p_object, BoxList): p_object.box_options.update(self.box_options) return p_object @@ -133,7 +146,7 @@ def _dotted_helper(self) -> List[str]: return keys def __repr__(self): - return f"BoxList({self.to_list()})" + return f"{self.__class__.__name__}({self.to_list()})" def __str__(self): return str(self.to_list()) diff --git a/box/config_box.py b/box/config_box.py index 0202ca3..4c48877 100644 --- a/box/config_box.py +++ b/box/config_box.py @@ -124,7 +124,7 @@ def getfloat(self, item, default=None): return self.float(item, default) def __repr__(self): - return "ConfigBox({0})".format(str(self.to_dict())) + return f"{self.__class__.__name__}({str(self.to_dict())})" def copy(self): return ConfigBox(super().copy()) diff --git a/box/shorthand_box.py b/box/shorthand_box.py index 99dfc8d..a82edbd 100644 --- a/box/shorthand_box.py +++ b/box/shorthand_box.py @@ -44,7 +44,7 @@ def toml(self) -> str: return self.to_toml() def __repr__(self): - return f"SBox({self})" + return f"{self.__class__.__name__}({self})" def copy(self) -> "SBox": return SBox(super(SBox, self).copy()) @@ -66,4 +66,4 @@ def __new__(cls, *args, **kwargs): return obj def __repr__(self) -> str: - return f"DDBox({self})" + return f"{self.__class__.__name__}({self})" diff --git a/test/test_box_list.py b/test/test_box_list.py index 536520f..e22c3a5 100644 --- a/test/test_box_list.py +++ b/test/test_box_list.py @@ -35,6 +35,10 @@ def test_box_list(self): assert new_list[-1].item == 22 new_list.append([{"bad_item": 33}]) assert new_list[-1][0].bad_item == 33 + new_list[-1].append([{"bad_item": 33}]) + assert new_list[-1, -1, 0].bad_item == 33 + bx = Box({0: {1: {2: {3: 3}}}, (0, 1, 2, 3): 4}) + assert bx[0, 1, 2, 3] == 4 assert repr(new_list).startswith("BoxList(") for x in new_list.to_list(): assert not isinstance(x, (BoxList, Box)) @@ -228,10 +232,8 @@ def test_no_recursion_errors(self): a.list_of_dicts.append([{"example2": 2}]) assert a["list_of_dicts"][1] == [{"example2": 2}] - def test_no_circular_references(self): - if sys.version_info >= (3, 12) and sys.platform == "win32": - pytest.skip("Windows fatal exception: stack overflow on github actions") + def test_circular_references(self): circular_list = [] circular_list.append(circular_list) - with pytest.raises(RecursionError): - BoxList(circular_list) + circular_box = BoxList(circular_list) + assert circular_box[0] == circular_box