Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-lexicon for openSUSE:Factory checked in at 2025-09-17 16:37:05 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-lexicon (Old) and /work/SRC/openSUSE:Factory/.python-lexicon.new.27445 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-lexicon" Wed Sep 17 16:37:05 2025 rev:9 rq:1305052 version:3.0.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-lexicon/python-lexicon.changes 2024-05-15 21:25:51.619992844 +0200 +++ /work/SRC/openSUSE:Factory/.python-lexicon.new.27445/python-lexicon.changes 2025-09-17 16:37:07.216849767 +0200 @@ -1,0 +2,9 @@ +Mon Sep 8 21:03:44 UTC 2025 - Sebastian Wagner <[email protected]> + +- Update to version 3.0.0: + - Dropped support for Python <3.9 + - Modernized project metadata re: Python interpreters, development + dependencies, etc. +- Remove obsolete support-pytest-8.patch + +------------------------------------------------------------------- @@ -62 +71 @@ -Sat Mar 3 10:38:55 UTC 2018 - [email protected] +Sat Mar 3 10:38:55 UTC 2018 - [email protected] Old: ---- lexicon-2.0.1.tar.gz support-pytest-8.patch New: ---- lexicon-3.0.0.tar.gz ----------(Old B)---------- Old: dependencies, etc. - Remove obsolete support-pytest-8.patch ----------(Old E)---------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-lexicon.spec ++++++ --- /var/tmp/diff_new_pack.SUgoSq/_old 2025-09-17 16:37:08.960923068 +0200 +++ /var/tmp/diff_new_pack.SUgoSq/_new 2025-09-17 16:37:08.976923735 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-lexicon # -# Copyright (c) 2024 SUSE LLC +# Copyright (c) 2025 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-lexicon -Version: 2.0.1 +Version: 3.0.0 Release: 0 Summary: Python dict subclass(es) with aliasing and attribute access License: BSD-2-Clause @@ -26,8 +26,7 @@ Source: https://files.pythonhosted.org/packages/source/l/lexicon/lexicon-%{version}.tar.gz # PATCH-FIX-UPSTREAM (sort of) Not in the sdist, but on GitHub Patch0: add-pytest-ini.patch -# PATCH-FIX-OPENSUSE Support pytest >= 8 -Patch1: support-pytest-8.patch +BuildRequires: %{python_module base >= 3.9} BuildRequires: %{python_module pip} BuildRequires: %{python_module setuptools} BuildRequires: %{python_module wheel} ++++++ lexicon-2.0.1.tar.gz -> lexicon-3.0.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lexicon-2.0.1/PKG-INFO new/lexicon-3.0.0/PKG-INFO --- old/lexicon-2.0.1/PKG-INFO 2021-09-10 20:38:28.000000000 +0200 +++ new/lexicon-3.0.0/PKG-INFO 2025-08-03 22:36:13.459291200 +0200 @@ -1,148 +1,153 @@ -Metadata-Version: 2.1 +Metadata-Version: 2.4 Name: lexicon -Version: 2.0.1 +Version: 3.0.0 Summary: Powerful dict subclass(es) with aliasing & attribute access -Home-page: https://github.com/bitprophet/lexicon#what -Author: Jeff Forcier -Author-email: [email protected] -License: BSD +Author-email: Jeff Forcier <[email protected]> +License-Expression: BSD-2-Clause +Project-URL: Homepage, https://github.com/bitprophet/lexicon#what Project-URL: Source, https://github.com/bitprophet/lexicon Project-URL: Changelog, https://github.com/bitprophet/lexicon/blob/main/docs/changelog.rst Project-URL: CI, https://app.circleci.com/pipelines/github/bitprophet/lexicon -Description: |version| |python| |license| |ci| |coverage| - - .. |version| image:: https://img.shields.io/pypi/v/lexicon - :target: https://pypi.org/project/lexicon/ - :alt: PyPI - Package Version - .. |python| image:: https://img.shields.io/pypi/pyversions/lexicon - :target: https://pypi.org/project/lexicon/ - :alt: PyPI - Python Version - .. |license| image:: https://img.shields.io/pypi/l/lexicon - :target: https://github.com/bitprophet/lexicon/blob/main/LICENSE - :alt: PyPI - License - .. |ci| image:: https://img.shields.io/circleci/build/github/bitprophet/lexicon/main - :target: https://app.circleci.com/pipelines/github/bitprophet/lexicon - :alt: CircleCI - .. |coverage| image:: https://img.shields.io/codecov/c/gh/bitprophet/lexicon - :target: https://app.codecov.io/gh/bitprophet/lexicon - :alt: Codecov - - WHAT - ==== - - Lexicon is a simple collection of Python ``dict`` subclasses providing extra - power: - - - ``AliasDict``, a dictionary supporting both simple and complex key aliasing: - - - Alias a single key to another key, so that e.g. ``mydict['bar']`` points - to ``mydict['foo']``, for both reads and writes. - - Alias a single key to a list of other keys, for writing only, e.g. with - ``active_groups = AliasDict({'ops': True, 'biz': True, 'dev': True, - 'product': True})`` one can make an alias ``'tech'`` mapping to ``('ops', - 'dev')`` and then e.g. ``active_groups['tech'] = False``. - - Aliasing is recursive: an alias pointing to another alias will behave as - if it points to the other alias' target. - - - ``AttributeDict``, supporting attribute read & write access, e.g. ``mydict = - AttributeDict({'foo': 'bar'})`` exhibits ``mydict.foo`` and ``mydict.foo = - 'new value'``. - - ``Lexicon``, a subclass of both of the above which exhibits both sets of - behavior. - - HOW - === - - - ``pip install lexicon`` - - ``from lexicon import Lexicon`` (or one of the superclasses) - - Use as needed. - - If you have a clone of the source repository, you can run the tests like so: - - - ``pip install -r dev-requirements.txt`` - - ``inv test`` - - API - === - - ``AliasDict`` - ------------- - - In all examples, ``'myalias'`` is the alias and ``'realkey'`` is the "real", - unaliased key. - - - ``alias(from_'myalias', to='realkey')``: Alias ``myalias`` to ``realkey`` so - ``d['myalias']`` behaves exactly like ``d['realkey']`` for both reads and - writes. - - - ``from_`` is the first keyword argument, but typically it can be omitted - and still reads fine. See below examples for this usage. See below for - details on how an alias affects other dict operations. - - - ``alias('myalias', to=('realkey', 'otherrealkey'))``: Alias ``myalias`` to - both ``realkey`` and ``otherrealkey``. As you might expect, this only works - well for writes, as there is never any guarantee that all targets of the - alias will contain the same value. - - ``unalias('myalias')``: Removes the ``myalias`` alias; any subsequent - reads/writes to ``myalias`` will behave as normal for a regular ``dict``. - - ``'myalias' in d`` (aka ``__contains__``): Returns True when given an alias, - so if ``myalias`` is an alias to some other key, dictionary membership tests - will behave as if ``myalias`` is set. - - ``del d['myalias']`` (aka ``__delitem__``): This effectively becomes ``del - d['realkey']`` -- to remove the alias itself, use ``unalias()``. - - ``del d['realkey']``: Deletes the real key/value pair (i.e. it calls - ``dict.__del__``) but doesn't touch any aliases pointing to ``realkey``. - - - As a result, "dangling" aliases pointing to nonexistent keys will raise - ``KeyError`` on access, but will continue working if the target key is - repopulated later. - - Caveats: - - - Because of the single-key/multi-key duality, ``AliasDict`` is incapable of - honoring non-string-type keys when aliasing (it must test ``isinstance(key, - basestring)`` to tell strings apart from non-string iterables). - - - ``AliasDict`` instances may still *use* non-string keys, of course -- it - just can't use them as alias targets. - - ``AttributeDict`` - ----------------- - - - ``d.key = 'value'`` (aka ``__setattr__``): Maps directly to ``d['key'] = - 'value'``. - - ``d.key`` (aka ``__getattr__``): Maps directly to ``d['key']``. - - ``del d.key`` (aka ``__delattr__``): Maps directly to ``del d['key']``. - - Collisions between "real" or pre-existing attributes, and - attributes-as-dict-keys, always results in the real attribute winning. Thus - it isn't possible to use attribute access to access e.g. ``d['get']``. - - ``Lexicon`` - ----------- - - Lexicon subclasses from ``AttributeDict`` first, then ``AliasDict``, with the - end result that attribute access will honor aliases. E.g.: - - d = Lexicon() - d.alias('myalias', to='realkey') - d.myalias = 'foo' - print d.realkey # prints 'foo' - -Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: BSD License -Classifier: Operating System :: MacOS :: MacOS X -Classifier: Operating System :: Unix -Classifier: Operating System :: POSIX Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 Classifier: Topic :: Software Development Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.9 Description-Content-Type: text/x-rst +License-File: LICENSE +Dynamic: license-file + +|version| |python| |license| |ci| |coverage| + +.. |version| image:: https://img.shields.io/pypi/v/lexicon + :target: https://pypi.org/project/lexicon/ + :alt: PyPI - Package Version +.. |python| image:: https://img.shields.io/pypi/pyversions/lexicon + :target: https://pypi.org/project/lexicon/ + :alt: PyPI - Python Version +.. |license| image:: https://img.shields.io/pypi/l/lexicon + :target: https://github.com/bitprophet/lexicon/blob/main/LICENSE + :alt: PyPI - License +.. |ci| image:: https://img.shields.io/circleci/build/github/bitprophet/lexicon/main + :target: https://app.circleci.com/pipelines/github/bitprophet/lexicon + :alt: CircleCI +.. |coverage| image:: https://img.shields.io/codecov/c/gh/bitprophet/lexicon + :target: https://app.codecov.io/gh/bitprophet/lexicon + :alt: Codecov + +WHAT +==== + +Lexicon is a simple collection of Python ``dict`` subclasses providing extra +power: + +- ``AliasDict``, a dictionary supporting both simple and complex key aliasing: + + - Alias a single key to another key, so that e.g. ``mydict['bar']`` points + to ``mydict['foo']``, for both reads and writes. + - Alias a single key to a list of other keys, for writing only, e.g. with + ``active_groups = AliasDict({'ops': True, 'biz': True, 'dev': True, + 'product': True})`` one can make an alias ``'tech'`` mapping to ``('ops', + 'dev')`` and then e.g. ``active_groups['tech'] = False``. + - Aliasing is recursive: an alias pointing to another alias will behave as + if it points to the other alias' target. + +- ``AttributeDict``, supporting attribute read & write access, e.g. ``mydict = + AttributeDict({'foo': 'bar'})`` exhibits ``mydict.foo`` and ``mydict.foo = + 'new value'``. +- ``Lexicon``, a subclass of both of the above which exhibits both sets of + behavior. + +HOW +=== + +Regular use: + +- ``pip install lexicon`` +- ``from lexicon import Lexicon`` (or one of the superclasses) +- Use as needed. + +Development/hacking/test suite: + +- Install dev dependencies via any of the following: + - ``pip install --group dev`` (assumes pip >= 25.1) + - ``uv sync`` + - anything else that understands PEP 735 dependency groups +- ``inv test`` (or ``inv --list`` to see other dev tasks) + +API +=== + +``AliasDict`` +------------- + +In all examples, ``'myalias'`` is the alias and ``'realkey'`` is the "real", +unaliased key. + +- ``alias(from_'myalias', to='realkey')``: Alias ``myalias`` to ``realkey`` so + ``d['myalias']`` behaves exactly like ``d['realkey']`` for both reads and + writes. + + - ``from_`` is the first keyword argument, but typically it can be omitted + and still reads fine. See below examples for this usage. See below for + details on how an alias affects other dict operations. + +- ``alias('myalias', to=('realkey', 'otherrealkey'))``: Alias ``myalias`` to + both ``realkey`` and ``otherrealkey``. As you might expect, this only works + well for writes, as there is never any guarantee that all targets of the + alias will contain the same value. +- ``unalias('myalias')``: Removes the ``myalias`` alias; any subsequent + reads/writes to ``myalias`` will behave as normal for a regular ``dict``. +- ``'myalias' in d`` (aka ``__contains__``): Returns True when given an alias, + so if ``myalias`` is an alias to some other key, dictionary membership tests + will behave as if ``myalias`` is set. +- ``del d['myalias']`` (aka ``__delitem__``): This effectively becomes ``del + d['realkey']`` -- to remove the alias itself, use ``unalias()``. +- ``del d['realkey']``: Deletes the real key/value pair (i.e. it calls + ``dict.__del__``) but doesn't touch any aliases pointing to ``realkey``. + + - As a result, "dangling" aliases pointing to nonexistent keys will raise + ``KeyError`` on access, but will continue working if the target key is + repopulated later. + +Caveats: + +- Because of the single-key/multi-key duality, ``AliasDict`` is incapable of + honoring non-string-type keys when aliasing (it must test ``isinstance(key, + basestring)`` to tell strings apart from non-string iterables). + + - ``AliasDict`` instances may still *use* non-string keys, of course -- it + just can't use them as alias targets. + +``AttributeDict`` +----------------- + +- ``d.key = 'value'`` (aka ``__setattr__``): Maps directly to ``d['key'] = + 'value'``. +- ``d.key`` (aka ``__getattr__``): Maps directly to ``d['key']``. +- ``del d.key`` (aka ``__delattr__``): Maps directly to ``del d['key']``. +- Collisions between "real" or pre-existing attributes, and + attributes-as-dict-keys, always results in the real attribute winning. Thus + it isn't possible to use attribute access to access e.g. ``d['get']``. + +``Lexicon`` +----------- + +Lexicon subclasses from ``AttributeDict`` first, then ``AliasDict``, with the +end result that attribute access will honor aliases. E.g.: + + d = Lexicon() + d.alias('myalias', to='realkey') + d.myalias = 'foo' + print d.realkey # prints 'foo' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lexicon-2.0.1/README.rst new/lexicon-3.0.0/README.rst --- old/lexicon-2.0.1/README.rst 2021-09-04 17:44:12.000000000 +0200 +++ new/lexicon-3.0.0/README.rst 2025-07-27 22:54:21.000000000 +0200 @@ -42,14 +42,19 @@ HOW === +Regular use: + - ``pip install lexicon`` - ``from lexicon import Lexicon`` (or one of the superclasses) - Use as needed. -If you have a clone of the source repository, you can run the tests like so: +Development/hacking/test suite: -- ``pip install -r dev-requirements.txt`` -- ``inv test`` +- Install dev dependencies via any of the following: + - ``pip install --group dev`` (assumes pip >= 25.1) + - ``uv sync`` + - anything else that understands PEP 735 dependency groups +- ``inv test`` (or ``inv --list`` to see other dev tasks) API === diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lexicon-2.0.1/dev-requirements.txt new/lexicon-3.0.0/dev-requirements.txt --- old/lexicon-2.0.1/dev-requirements.txt 2021-09-04 00:16:37.000000000 +0200 +++ new/lexicon-3.0.0/dev-requirements.txt 1970-01-01 01:00:00.000000000 +0100 @@ -1,23 +0,0 @@ -# Development requirements. There are no runtime/install requirements. - -# Docs (changelog) -releases==1.6.3 -Sphinx>=1.4,<1.7 -# Testing -pytest-relaxed==1.1.5 -# Coverage -pytest-cov==2.11.1 -codecov==2.1.11 -# Invoke, invocations for release shenanigans etc -invoke==1.6.0 -invocations==2.2.0 -# Release deps -wheel==0.36.2 -twine==1.15.0 -readme_renderer==29.0 -# Formatting -black==19.10b0 -# Linting -flake8==3.6.0 -# Ourselves --e . diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lexicon-2.0.1/docs/changelog.rst new/lexicon-3.0.0/docs/changelog.rst --- old/lexicon-2.0.1/docs/changelog.rst 2021-09-10 20:30:36.000000000 +0200 +++ new/lexicon-3.0.0/docs/changelog.rst 2025-08-03 22:35:34.000000000 +0200 @@ -2,8 +2,12 @@ Changelog ========= +- :release:`3.0.0 <2025-08-03>` +- :support:`-` Dropped support for Python <3.9 +- :support:`-` Modernized project metadata re: Python interpreters, development + dependencies, etc. - :release:`2.0.1 <2021-09-10>` -- :support:`- backported` Fix up some project metadata. +- :support:`- backported` Fixed up some project metadata. - :release:`2.0.0 <2021-09-10>` - :support:`-` Dropped support for Python <3.6 - :support:`-` Added a ``_version`` submodule and imported its diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lexicon-2.0.1/docs/conf.py new/lexicon-3.0.0/docs/conf.py --- old/lexicon-2.0.1/docs/conf.py 2021-07-09 23:11:54.000000000 +0200 +++ new/lexicon-3.0.0/docs/conf.py 2025-05-12 03:02:24.000000000 +0200 @@ -15,9 +15,9 @@ exclude_patterns = ["_build"] default_role = "obj" -project = u"Lexicon" +project = "Lexicon" year = datetime.now().year -copyright = u"%d Jeff Forcier" % year +copyright = "%d Jeff Forcier" % year # Ensure project directory is on PYTHONPATH for version, autodoc access sys.path.insert(0, abspath(join(getcwd(), ".."))) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lexicon-2.0.1/lexicon/__init__.py new/lexicon-3.0.0/lexicon/__init__.py --- old/lexicon-2.0.1/lexicon/__init__.py 2021-07-09 00:11:33.000000000 +0200 +++ new/lexicon-3.0.0/lexicon/__init__.py 2025-08-03 22:07:33.000000000 +0200 @@ -1,4 +1,6 @@ -from ._version import __version_info__, __version__ # noqa +from importlib import metadata +__version__ = metadata.version("lexicon") + from .attribute_dict import AttributeDict from .alias_dict import AliasDict @@ -21,4 +23,4 @@ # Alias and Attribute Dicts, so not solvable in a parent alone.) if key == "aliases" and key not in self.__dict__: self.__dict__[key] = {} - return super(Lexicon, self).__getattr__(key) + return super().__getattr__(key) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lexicon-2.0.1/lexicon/_version.py new/lexicon-3.0.0/lexicon/_version.py --- old/lexicon-2.0.1/lexicon/_version.py 2021-09-10 20:30:41.000000000 +0200 +++ new/lexicon-3.0.0/lexicon/_version.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,2 +0,0 @@ -__version_info__ = (2, 0, 1) -__version__ = ".".join(map(str, __version_info__)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lexicon-2.0.1/lexicon/alias_dict.py new/lexicon-3.0.0/lexicon/alias_dict.py --- old/lexicon-2.0.1/lexicon/alias_dict.py 2021-07-08 23:39:27.000000000 +0200 +++ new/lexicon-3.0.0/lexicon/alias_dict.py 2025-05-11 23:40:09.000000000 +0200 @@ -1,6 +1,6 @@ class AliasDict(dict): def __init__(self, *args, **kwargs): - super(AliasDict, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.aliases = {} def alias(self, from_, to): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lexicon-2.0.1/lexicon.egg-info/PKG-INFO new/lexicon-3.0.0/lexicon.egg-info/PKG-INFO --- old/lexicon-2.0.1/lexicon.egg-info/PKG-INFO 2021-09-10 20:38:28.000000000 +0200 +++ new/lexicon-3.0.0/lexicon.egg-info/PKG-INFO 2025-08-03 22:36:13.000000000 +0200 @@ -1,148 +1,153 @@ -Metadata-Version: 2.1 +Metadata-Version: 2.4 Name: lexicon -Version: 2.0.1 +Version: 3.0.0 Summary: Powerful dict subclass(es) with aliasing & attribute access -Home-page: https://github.com/bitprophet/lexicon#what -Author: Jeff Forcier -Author-email: [email protected] -License: BSD +Author-email: Jeff Forcier <[email protected]> +License-Expression: BSD-2-Clause +Project-URL: Homepage, https://github.com/bitprophet/lexicon#what Project-URL: Source, https://github.com/bitprophet/lexicon Project-URL: Changelog, https://github.com/bitprophet/lexicon/blob/main/docs/changelog.rst Project-URL: CI, https://app.circleci.com/pipelines/github/bitprophet/lexicon -Description: |version| |python| |license| |ci| |coverage| - - .. |version| image:: https://img.shields.io/pypi/v/lexicon - :target: https://pypi.org/project/lexicon/ - :alt: PyPI - Package Version - .. |python| image:: https://img.shields.io/pypi/pyversions/lexicon - :target: https://pypi.org/project/lexicon/ - :alt: PyPI - Python Version - .. |license| image:: https://img.shields.io/pypi/l/lexicon - :target: https://github.com/bitprophet/lexicon/blob/main/LICENSE - :alt: PyPI - License - .. |ci| image:: https://img.shields.io/circleci/build/github/bitprophet/lexicon/main - :target: https://app.circleci.com/pipelines/github/bitprophet/lexicon - :alt: CircleCI - .. |coverage| image:: https://img.shields.io/codecov/c/gh/bitprophet/lexicon - :target: https://app.codecov.io/gh/bitprophet/lexicon - :alt: Codecov - - WHAT - ==== - - Lexicon is a simple collection of Python ``dict`` subclasses providing extra - power: - - - ``AliasDict``, a dictionary supporting both simple and complex key aliasing: - - - Alias a single key to another key, so that e.g. ``mydict['bar']`` points - to ``mydict['foo']``, for both reads and writes. - - Alias a single key to a list of other keys, for writing only, e.g. with - ``active_groups = AliasDict({'ops': True, 'biz': True, 'dev': True, - 'product': True})`` one can make an alias ``'tech'`` mapping to ``('ops', - 'dev')`` and then e.g. ``active_groups['tech'] = False``. - - Aliasing is recursive: an alias pointing to another alias will behave as - if it points to the other alias' target. - - - ``AttributeDict``, supporting attribute read & write access, e.g. ``mydict = - AttributeDict({'foo': 'bar'})`` exhibits ``mydict.foo`` and ``mydict.foo = - 'new value'``. - - ``Lexicon``, a subclass of both of the above which exhibits both sets of - behavior. - - HOW - === - - - ``pip install lexicon`` - - ``from lexicon import Lexicon`` (or one of the superclasses) - - Use as needed. - - If you have a clone of the source repository, you can run the tests like so: - - - ``pip install -r dev-requirements.txt`` - - ``inv test`` - - API - === - - ``AliasDict`` - ------------- - - In all examples, ``'myalias'`` is the alias and ``'realkey'`` is the "real", - unaliased key. - - - ``alias(from_'myalias', to='realkey')``: Alias ``myalias`` to ``realkey`` so - ``d['myalias']`` behaves exactly like ``d['realkey']`` for both reads and - writes. - - - ``from_`` is the first keyword argument, but typically it can be omitted - and still reads fine. See below examples for this usage. See below for - details on how an alias affects other dict operations. - - - ``alias('myalias', to=('realkey', 'otherrealkey'))``: Alias ``myalias`` to - both ``realkey`` and ``otherrealkey``. As you might expect, this only works - well for writes, as there is never any guarantee that all targets of the - alias will contain the same value. - - ``unalias('myalias')``: Removes the ``myalias`` alias; any subsequent - reads/writes to ``myalias`` will behave as normal for a regular ``dict``. - - ``'myalias' in d`` (aka ``__contains__``): Returns True when given an alias, - so if ``myalias`` is an alias to some other key, dictionary membership tests - will behave as if ``myalias`` is set. - - ``del d['myalias']`` (aka ``__delitem__``): This effectively becomes ``del - d['realkey']`` -- to remove the alias itself, use ``unalias()``. - - ``del d['realkey']``: Deletes the real key/value pair (i.e. it calls - ``dict.__del__``) but doesn't touch any aliases pointing to ``realkey``. - - - As a result, "dangling" aliases pointing to nonexistent keys will raise - ``KeyError`` on access, but will continue working if the target key is - repopulated later. - - Caveats: - - - Because of the single-key/multi-key duality, ``AliasDict`` is incapable of - honoring non-string-type keys when aliasing (it must test ``isinstance(key, - basestring)`` to tell strings apart from non-string iterables). - - - ``AliasDict`` instances may still *use* non-string keys, of course -- it - just can't use them as alias targets. - - ``AttributeDict`` - ----------------- - - - ``d.key = 'value'`` (aka ``__setattr__``): Maps directly to ``d['key'] = - 'value'``. - - ``d.key`` (aka ``__getattr__``): Maps directly to ``d['key']``. - - ``del d.key`` (aka ``__delattr__``): Maps directly to ``del d['key']``. - - Collisions between "real" or pre-existing attributes, and - attributes-as-dict-keys, always results in the real attribute winning. Thus - it isn't possible to use attribute access to access e.g. ``d['get']``. - - ``Lexicon`` - ----------- - - Lexicon subclasses from ``AttributeDict`` first, then ``AliasDict``, with the - end result that attribute access will honor aliases. E.g.: - - d = Lexicon() - d.alias('myalias', to='realkey') - d.myalias = 'foo' - print d.realkey # prints 'foo' - -Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: BSD License -Classifier: Operating System :: MacOS :: MacOS X -Classifier: Operating System :: Unix -Classifier: Operating System :: POSIX Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 Classifier: Topic :: Software Development Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.9 Description-Content-Type: text/x-rst +License-File: LICENSE +Dynamic: license-file + +|version| |python| |license| |ci| |coverage| + +.. |version| image:: https://img.shields.io/pypi/v/lexicon + :target: https://pypi.org/project/lexicon/ + :alt: PyPI - Package Version +.. |python| image:: https://img.shields.io/pypi/pyversions/lexicon + :target: https://pypi.org/project/lexicon/ + :alt: PyPI - Python Version +.. |license| image:: https://img.shields.io/pypi/l/lexicon + :target: https://github.com/bitprophet/lexicon/blob/main/LICENSE + :alt: PyPI - License +.. |ci| image:: https://img.shields.io/circleci/build/github/bitprophet/lexicon/main + :target: https://app.circleci.com/pipelines/github/bitprophet/lexicon + :alt: CircleCI +.. |coverage| image:: https://img.shields.io/codecov/c/gh/bitprophet/lexicon + :target: https://app.codecov.io/gh/bitprophet/lexicon + :alt: Codecov + +WHAT +==== + +Lexicon is a simple collection of Python ``dict`` subclasses providing extra +power: + +- ``AliasDict``, a dictionary supporting both simple and complex key aliasing: + + - Alias a single key to another key, so that e.g. ``mydict['bar']`` points + to ``mydict['foo']``, for both reads and writes. + - Alias a single key to a list of other keys, for writing only, e.g. with + ``active_groups = AliasDict({'ops': True, 'biz': True, 'dev': True, + 'product': True})`` one can make an alias ``'tech'`` mapping to ``('ops', + 'dev')`` and then e.g. ``active_groups['tech'] = False``. + - Aliasing is recursive: an alias pointing to another alias will behave as + if it points to the other alias' target. + +- ``AttributeDict``, supporting attribute read & write access, e.g. ``mydict = + AttributeDict({'foo': 'bar'})`` exhibits ``mydict.foo`` and ``mydict.foo = + 'new value'``. +- ``Lexicon``, a subclass of both of the above which exhibits both sets of + behavior. + +HOW +=== + +Regular use: + +- ``pip install lexicon`` +- ``from lexicon import Lexicon`` (or one of the superclasses) +- Use as needed. + +Development/hacking/test suite: + +- Install dev dependencies via any of the following: + - ``pip install --group dev`` (assumes pip >= 25.1) + - ``uv sync`` + - anything else that understands PEP 735 dependency groups +- ``inv test`` (or ``inv --list`` to see other dev tasks) + +API +=== + +``AliasDict`` +------------- + +In all examples, ``'myalias'`` is the alias and ``'realkey'`` is the "real", +unaliased key. + +- ``alias(from_'myalias', to='realkey')``: Alias ``myalias`` to ``realkey`` so + ``d['myalias']`` behaves exactly like ``d['realkey']`` for both reads and + writes. + + - ``from_`` is the first keyword argument, but typically it can be omitted + and still reads fine. See below examples for this usage. See below for + details on how an alias affects other dict operations. + +- ``alias('myalias', to=('realkey', 'otherrealkey'))``: Alias ``myalias`` to + both ``realkey`` and ``otherrealkey``. As you might expect, this only works + well for writes, as there is never any guarantee that all targets of the + alias will contain the same value. +- ``unalias('myalias')``: Removes the ``myalias`` alias; any subsequent + reads/writes to ``myalias`` will behave as normal for a regular ``dict``. +- ``'myalias' in d`` (aka ``__contains__``): Returns True when given an alias, + so if ``myalias`` is an alias to some other key, dictionary membership tests + will behave as if ``myalias`` is set. +- ``del d['myalias']`` (aka ``__delitem__``): This effectively becomes ``del + d['realkey']`` -- to remove the alias itself, use ``unalias()``. +- ``del d['realkey']``: Deletes the real key/value pair (i.e. it calls + ``dict.__del__``) but doesn't touch any aliases pointing to ``realkey``. + + - As a result, "dangling" aliases pointing to nonexistent keys will raise + ``KeyError`` on access, but will continue working if the target key is + repopulated later. + +Caveats: + +- Because of the single-key/multi-key duality, ``AliasDict`` is incapable of + honoring non-string-type keys when aliasing (it must test ``isinstance(key, + basestring)`` to tell strings apart from non-string iterables). + + - ``AliasDict`` instances may still *use* non-string keys, of course -- it + just can't use them as alias targets. + +``AttributeDict`` +----------------- + +- ``d.key = 'value'`` (aka ``__setattr__``): Maps directly to ``d['key'] = + 'value'``. +- ``d.key`` (aka ``__getattr__``): Maps directly to ``d['key']``. +- ``del d.key`` (aka ``__delattr__``): Maps directly to ``del d['key']``. +- Collisions between "real" or pre-existing attributes, and + attributes-as-dict-keys, always results in the real attribute winning. Thus + it isn't possible to use attribute access to access e.g. ``d['get']``. + +``Lexicon`` +----------- + +Lexicon subclasses from ``AttributeDict`` first, then ``AliasDict``, with the +end result that attribute access will honor aliases. E.g.: + + d = Lexicon() + d.alias('myalias', to='realkey') + d.myalias = 'foo' + print d.realkey # prints 'foo' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lexicon-2.0.1/lexicon.egg-info/SOURCES.txt new/lexicon-3.0.0/lexicon.egg-info/SOURCES.txt --- old/lexicon-2.0.1/lexicon.egg-info/SOURCES.txt 2021-09-10 20:38:28.000000000 +0200 +++ new/lexicon-3.0.0/lexicon.egg-info/SOURCES.txt 2025-08-03 22:36:13.000000000 +0200 @@ -1,14 +1,11 @@ LICENSE MANIFEST.in README.rst -dev-requirements.txt -setup.cfg -setup.py +pyproject.toml docs/changelog.rst docs/conf.py docs/index.rst lexicon/__init__.py -lexicon/_version.py lexicon/alias_dict.py lexicon/attribute_dict.py lexicon.egg-info/PKG-INFO diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lexicon-2.0.1/pyproject.toml new/lexicon-3.0.0/pyproject.toml --- old/lexicon-2.0.1/pyproject.toml 1970-01-01 01:00:00.000000000 +0100 +++ new/lexicon-3.0.0/pyproject.toml 2025-08-03 22:18:49.000000000 +0200 @@ -0,0 +1,84 @@ +[project] +name = "lexicon" +description = "Powerful dict subclass(es) with aliasing & attribute access" +requires-python = ">=3.9" +license = "BSD-2-Clause" +version = "3.0.0" + +authors = [{name = "Jeff Forcier", email = "[email protected]"}] +# NOTE: this seems to 'just work' these days? no need for manually dynamic +# long_description anymore; pypi appears to slurp up what this refers to & use +# it as the readme on the release/project page? +readme = "README.rst" +license-files = ["LICENSE"] + +dependencies = [] + +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "Intended Audience :: Developers", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Topic :: Software Development", + "Topic :: Software Development :: Libraries", + "Topic :: Software Development :: Libraries :: Python Modules", +] + + +# Sadly, my usual set of URLs isn't fully covered by either the 'well-known' or +# PyPI-specific sets, so I just use fully human-readable keys that line up with +# one or both of those when possible, and accept the rest won't get special +# icon treatment on PyPI or etc. +[project.urls] +Homepage = "https://github.com/bitprophet/lexicon#what" +Source = "https://github.com/bitprophet/lexicon" +Changelog = "https://github.com/bitprophet/lexicon/blob/main/docs/changelog.rst" +CI = "https://app.circleci.com/pipelines/github/bitprophet/lexicon" + + +# Being explicit is good; this way we're not leaving it up to a given +# frontend's default choices (though uv, the one I'm most likely to use, does +# default to hatchling.) +[build-system] +# Tried using hatchling at one point but a) setuptools still feels more widely +# used/banged-upon + b) hatchling's version tooling was confusing/broken for +# me. +requires = ["setuptools >= 77"] +build-backend = "setuptools.build_meta" + +[dependency-groups] +# Replaces dev-requirements.txt. depending on frontend: +# - uv: automatically uses 'dev' for install-but-not-publish, cool. +# - pip >=25.1: can say `pip install --group dev` +# - hatch: doesn't seem to support yet (had/has its own grouping feature?) +# - pdm: no idea +dev = [ + # Docs (changelog) + "releases>=2.1,<3", + "Sphinx>=5.3,<6", + # Above Sphinx version needs imghdr, removed from stdlib in 3.11ish + "standard-imghdr==3.13.0", + # Testing + "pytest-relaxed>=2", + # Coverage + "pytest-cov==2.11.1", + "codecov==2.1.13", + # Invoke, invocations for release shenanigans etc + "invoke>=2", + "invocations>=4", + # Formatting + "black==23.3.0", + # Linting + "flake8~=7.3.0", + # NOTE: no more "-e ." as in requirements.txt days - most tools, like uv, + # will automatically install the project itself (in editable mode) when + # syncing deps. +] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lexicon-2.0.1/setup.cfg new/lexicon-3.0.0/setup.cfg --- old/lexicon-2.0.1/setup.cfg 2021-09-10 20:38:28.000000000 +0200 +++ new/lexicon-3.0.0/setup.cfg 2025-08-03 22:36:13.459484000 +0200 @@ -1,9 +1,3 @@ -[bdist_wheel] -universal = 1 - -[metadata] -license_file = LICENSE - [egg_info] tag_build = tag_date = 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lexicon-2.0.1/setup.py new/lexicon-3.0.0/setup.py --- old/lexicon-2.0.1/setup.py 2021-09-10 20:31:50.000000000 +0200 +++ new/lexicon-3.0.0/setup.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,51 +0,0 @@ -#!/usr/bin/env python - -import os - -from setuptools import setup - -# Version info -- read without importing -_locals = {} -with open("lexicon/_version.py") as fp: - exec(fp.read(), None, _locals) -version = _locals["__version__"] - -# Readme -with open(os.path.join(os.path.dirname(__file__), "README.rst")) as fd: - long_description = fd.read() - -setup( - name="lexicon", - version=version, - description="Powerful dict subclass(es) with aliasing & attribute access", - license="BSD", - long_description=long_description, - long_description_content_type="text/x-rst", - author="Jeff Forcier", - author_email="[email protected]", - url="https://github.com/bitprophet/lexicon#what", - project_urls={ - "Source": "https://github.com/bitprophet/lexicon", - "Changelog": "https://github.com/bitprophet/lexicon/blob/main/docs/changelog.rst", - "CI": "https://app.circleci.com/pipelines/github/bitprophet/lexicon", - }, - packages=["lexicon"], - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Environment :: Console", - "Intended Audience :: Developers", - "License :: OSI Approved :: BSD License", - "Operating System :: MacOS :: MacOS X", - "Operating System :: Unix", - "Operating System :: POSIX", - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Topic :: Software Development", - "Topic :: Software Development :: Libraries", - "Topic :: Software Development :: Libraries :: Python Modules", - ], -) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lexicon-2.0.1/tests/alias_dict.py new/lexicon-3.0.0/tests/alias_dict.py --- old/lexicon-2.0.1/tests/alias_dict.py 2021-07-08 23:39:27.000000000 +0200 +++ new/lexicon-3.0.0/tests/alias_dict.py 2025-05-11 23:40:10.000000000 +0200 @@ -36,7 +36,7 @@ ad.unalias("lol no") class aliases_of: - def setup(self): + def setup_method(self): self.ad = AliasDict() def returns_list_of_aliases_for_given_real_key(self): @@ -162,7 +162,7 @@ class aliases_are_not_real_keys: "aliases are not real keys" - def setup(self): + def setup_method(self): self.a = AliasDict({"key1": "val1", "key2": "val2"}) self.a.alias("myalias", "key1")
