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")
 

Reply via email to