Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-typedload for 
openSUSE:Factory checked in at 2026-03-04 21:08:41
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-typedload (Old)
 and      /work/SRC/openSUSE:Factory/.python-typedload.new.561 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-typedload"

Wed Mar  4 21:08:41 2026 rev:8 rq:1336246 version:2.40

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-typedload/python-typedload.changes        
2025-12-11 18:40:49.840185572 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-typedload.new.561/python-typedload.changes   
    2026-03-04 21:09:32.587173100 +0100
@@ -1,0 +2,13 @@
+Tue Mar  3 23:27:34 UTC 2026 - Dirk Mรผller <[email protected]>
+
+- update to 2.40:
+  * Fix corner case of is_optional
+  * Drop LGPL3 license exception for Appgate Cybersecurity, Inc.
+  * The changes in this version do not carry the exception, so as a
+    result the entire project is GPL3 licensed.
+    LGPL3 allows relicensing under GPL3, but not viceversa.
+  * Improve documentation and examples
+  * Added moretypes module for types I might find convenient to
+    have Added HexRGB to the moretypes module
+
+-------------------------------------------------------------------

Old:
----
  typedload_2.39.orig.tar.gz

New:
----
  typedload_2.40.orig.tar.gz

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

Other differences:
------------------
++++++ python-typedload.spec ++++++
--- /var/tmp/diff_new_pack.klVOQN/_old  2026-03-04 21:09:33.419207488 +0100
+++ /var/tmp/diff_new_pack.klVOQN/_new  2026-03-04 21:09:33.419207488 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-typedload
 #
-# Copyright (c) 2025 SUSE LLC and contributors
+# Copyright (c) 2026 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
@@ -17,7 +17,7 @@
 
 
 Name:           python-typedload
-Version:        2.39
+Version:        2.40
 Release:        0
 Summary:        Load and dump data from json-like format into typed data 
structures
 License:        GPL-3.0-only

++++++ typedload_2.39.orig.tar.gz -> typedload_2.40.orig.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/typedload/LICENSE new/typedload/LICENSE
--- old/typedload/LICENSE       2025-10-21 08:51:45.000000000 +0200
+++ new/typedload/LICENSE       2026-02-10 20:16:16.000000000 +0100
@@ -1,8 +1,6 @@
 This software is released under the GNU General Public License 3.
 
-An exception to this is granted to Appgate Cybersecurity, Inc., which
-is allowed to use this software under the GNU Lesser General Public
-License 3. Because the author works there.
+GNU Lesser General Public License 3 can be granted, but none is in place.
 
 Verbatim text of GNU GPL 3 and GNU LGPL 3 follows.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/typedload/Makefile new/typedload/Makefile
--- old/typedload/Makefile      2025-10-21 08:51:45.000000000 +0200
+++ new/typedload/Makefile      2026-02-10 23:39:12.000000000 +0100
@@ -116,7 +116,6 @@
                docs/errors.md \
                docs/examples.md \
                docs/gpl3logo.png \
-               docs/origin_story.md \
                docs/performance.md \
                docs/README.md \
                docs/SECURITY.md \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/typedload/docs/CHANGELOG.md 
new/typedload/docs/CHANGELOG.md
--- old/typedload/docs/CHANGELOG.md     2025-10-21 09:03:19.000000000 +0200
+++ new/typedload/docs/CHANGELOG.md     2026-02-11 09:33:45.000000000 +0100
@@ -1,3 +1,14 @@
+2.40
+====
+* Fix corner case of is_optional
+* Drop LGPL3 license exception for Appgate Cybersecurity, Inc.
+  The changes in this version do not carry the exception, so as a
+  result the entire project is GPL3 licensed.
+  LGPL3 allows relicensing under GPL3, but not viceversa.
+* Improve documentation and examples
+* Added moretypes module for types I might find convenient to have
+* Added HexRGB to the moretypes module
+
 2.39
 ====
 * Re-release
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/typedload/docs/CONTRIBUTING.md 
new/typedload/docs/CONTRIBUTING.md
--- old/typedload/docs/CONTRIBUTING.md  2025-10-21 08:51:45.000000000 +0200
+++ new/typedload/docs/CONTRIBUTING.md  2026-02-10 20:16:16.000000000 +0100
@@ -1,18 +1,25 @@
 Contributing
 ============
 
+How
+---
+
 All contributions must pass the test suite and must generate no warnings with 
the latest available version of mypy.
 
 The best way of sending changes is to use git-send-mail to [email protected]
 
 It is acceptable also to use codeberg's pull request functionality.
 
-Contributors must accept that their changes can use both GPL3 and LGPL3.
 
-Currently the license is GPL3 with one exception being made for the company 
where I work. In the future more LGPL3 exception could be made, but no other 
license than those will be used.
+Copyright and license
+---------------------
+
+Contributors retain the copyright of their contributions. They must accept 
that the project uses GPL3 and LGPL3 licenses; but their contributions must be 
under LGPL3.
+
+As a consequence the owners of the project can grant LGPL3 exceptions by 
giving permission to use the LGPL3 license to someone. But in general the 
owners of the project grant the use under GPL3.
 
-In the event that new versions of GPL and LGPL licenses should be published by 
the Free Software Foundation (FSF) in the future, contributors must accept that 
their contribution might be licensed with those future versions of GPL and LGPL 
in addition to the current version.
+Contributors have no control on who gets LGPL3 exceptions, but by retaining 
copyright they know the project will not be able to change license to anything 
more permissive than LGPL3.
 
-This will be decided by the owners of the project if and when new versions of 
the licenses are created and is not automatic, to prevent the case where a new 
board of the FSF should decide to abandon its mission and grant less freedom to 
the users.
+Contributors contributing on behalf of companies are required to disclose the 
fact and to assign their copyright to the project owners. This can be done in a 
signed commit message.
 
-For new versions that aim at fixing corner cases (such as version 3), they 
will be used and the software will be available under multiple licenses 
versions (starting from 3). They will not be adopted should the FSF decide that 
GPL4 should be a non-copyleft license or other similar spirit altering changes.
+Contributors must accept that the project might move to future versions of the 
GPL and LGPL, published by the Free Software Foundation, but that will not 
happen automatically and the decision will be made by the project owners if 
that should happen.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/typedload/docs/errors.md new/typedload/docs/errors.md
--- old/typedload/docs/errors.md        2025-10-21 08:51:45.000000000 +0200
+++ new/typedload/docs/errors.md        2026-02-10 23:39:12.000000000 +0100
@@ -83,9 +83,9 @@
 Union
 -----
 
-Because it is normal for a union of n types to generate n-1 exceptions, a 
union which fails generated n exceptions.
+Since it is normal for a union of n types to generate n-1 exceptions, a union 
which fails generated n exceptions.
 
-Typedload has no way of knowing which of those is the important exception that 
was expected to succeed and instead puts all the exceptions inside the 
`exception` field of the parent exception.
+Typedload has no way of discerning which exception is actually relevant, 
instead it stores all the exceptions inside the `exception` field of the parent 
exception.
 
 So all the sub exceptions can be investigated to decide which one is the most 
relevant one.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/typedload/docs/origin_story.md 
new/typedload/docs/origin_story.md
--- old/typedload/docs/origin_story.md  2025-10-21 08:51:45.000000000 +0200
+++ new/typedload/docs/origin_story.md  1970-01-01 01:00:00.000000000 +0100
@@ -1,20 +0,0 @@
-typedload's origin story
-========================
-
-At $DAYJOB there was a software written in Scala, that worked by mapping 
objects into mongodb data.
-
-The only one person there knowing Scala decided to quit, and so we were in the 
process of rewriting the entire thing in Python.
-
-I had been tasked to write a few hundreds methods `to_json()` and 
`from_json()` for all the various objects that are used by this software.
-
-Since I thought it was going to be a terribly boring job in which I'd make 
tens of typos, I looked for a library that did such a thing. But of course none 
existed, so I started writing a module to do that.
-
-The `to_json()` part was rather easy, while the opposite wasn't, anyway after 
a few days the module seemed to be working nicely.
-
-In fact the module worked so nicely that I wanted to use it in my personal 
projects as well. However I couldn't, because $DAYJOB owned the copyright, and 
I knew that getting the authorization to release it as open source would take 
from 2 years to +โˆž.
-
-So, I just wrote a stand alone library outside of work to do the exact same 
thing, but in a more generic and flexible way, rather than tied to the specific 
software we had at $DAYJOB.
-
-The result of writing the same library twice was that the second time around 
it came out better, and so the original version got completely discarded and at 
$DAYJOB typedload is now in use.
-
-At the time pydantic existed but it was not available on any distribution and 
was not production quality. I am a lazy person so if it had been usable I would 
have used it instead. But in the end I think typedload is better. But that's 
just my own opinion.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/typedload/docs/performance.md 
new/typedload/docs/performance.md
--- old/typedload/docs/performance.md   2025-10-21 08:51:45.000000000 +0200
+++ new/typedload/docs/performance.md   2026-02-10 23:39:12.000000000 +0100
@@ -6,7 +6,7 @@
 The tests are done on my PC. The following libraries are tested:
 
 * `typedload`, the 3 most recent versions. It shines with tagged unions, which 
is what I mostly use.
-* `pydantic2` years of work to rewrite it in Rust, [implemented detection of 
tagged unions years after I did 
it](https://github.com/pydantic/pydantic/issues/5163#issuecomment-1619203179), 
still managing to lose some benchmarks ๐Ÿ˜… (sorry I find it hilarious that a 
company hiring a team of developers to do a Rust implementation cannot beat 
*all* the benchmarks when typedload is pure python and I work on it every once 
in a while).
+* `pydantic2` after years of work to rewrite it in Rust, [implemented 
detection of tagged unions years after I did 
it](https://github.com/pydantic/pydantic/issues/5163#issuecomment-1619203179), 
still managing to lose some benchmarks ๐Ÿ˜… (sorry I find it hilarious that a 
company hiring a team of developers to do a Rust implementation cannot beat 
*all* the benchmarks when typedload is pure python and I work on it every once 
in a while).
 * `apischema` is slower where there are unions, faster otherwise
 
 Using Python 3.13
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/typedload/docs/supported_types.md 
new/typedload/docs/supported_types.md
--- old/typedload/docs/supported_types.md       2025-10-21 08:51:45.000000000 
+0200
+++ new/typedload/docs/supported_types.md       2026-02-11 09:33:45.000000000 
+0100
@@ -565,3 +565,8 @@
 
 Loads a str or bytes as a compiled Pattern object by passing through 
re.compile.
 When dumping gives back the original str or bytes pattern.
+
+typedload.moretypes.HexRGB
+--------------------------
+
+This class loads and dumps as a string, but also validates that the string is 
a valid RGB colour in the form '#nnnnnn'.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/typedload/example.py new/typedload/example.py
--- old/typedload/example.py    2025-10-21 08:51:45.000000000 +0200
+++ new/typedload/example.py    2026-02-10 23:39:12.000000000 +0100
@@ -1,7 +1,7 @@
 #!/usr/bin/python3
 
 # typedload
-# Copyright (C) 2020-2024 Salvo "LtWorf" Tomaselli
+# Copyright (C) 2020-2026 Salvo "LtWorf" Tomaselli
 #
 # typedload is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -32,7 +32,7 @@
 from datetime import datetime
 from uuid import UUID
 import json
-from typing import *
+from typing import Any, NamedTuple
 import urllib.request
 
 import typedload
@@ -40,8 +40,8 @@
 
 class CommandLine(NamedTuple):
     full: bool
-    project: Optional[str]
-    username: Optional[str]
+    project: str | None
+    username: str | None
 
     def get_url(self) -> str:
         if self.username is None and self.project is None:
@@ -86,7 +86,7 @@
     created_at: datetime
     published_at: datetime
     author: User
-    assets: List[Asset]
+    assets: list[Asset]
 
 
 def get_data(args: CommandLine) -> Any:
@@ -98,14 +98,14 @@
         return json.load(f)
 
 
-def print_report(data: List[Release], args: CommandLine):
+def print_report(data: list[Release], args: CommandLine) -> None:
     for i in data:
         if i.draft or i.prerelease:
             continue
-        print('Release:', i.name, end=' ')
+        print(f'Release: "{i.name}"', end=' ')
 
         if args.full:
-            print('Created by:', i.author.login, 'on:', i.created_at)
+            print(f'Created by: {i.author.login} on: {i.created_at}')
         else:
             print()
 
@@ -114,8 +114,8 @@
                 print('\t%d\t%s' % (asset.download_count, asset.name))
 
 
-def main():
-    parser = argparse.ArgumentParser()
+def main() -> None:
+    parser = argparse.ArgumentParser(description='Query the codeberg API to 
list the releases and download counts of projects')
     parser.add_argument('-u', '--username', help='The username to query')
     parser.add_argument('-p', '--project', help='The project to query')
     parser.add_argument('-f', '--full', help='Print the full report', 
action='store_true')
@@ -124,12 +124,10 @@
     args = typedload.load(parser.parse_args(), CommandLine)
     data = get_data(args)
 
-    # Github returns dates like this "2016-08-23T18:26:00Z", which are not 
supported by typedload
-    # So we make a custom handler for them.
     loader = typedload.dataloader.Loader()
 
     # We know what the API returns so we can load the json into typed data
-    typed_data = loader.load(data, List[Release])
+    typed_data = loader.load(data, list[Release])
     print_report(typed_data, args)
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/typedload/mkdocs.yml new/typedload/mkdocs.yml
--- old/typedload/mkdocs.yml    2025-10-21 08:51:45.000000000 +0200
+++ new/typedload/mkdocs.yml    2026-02-10 23:39:12.000000000 +0100
@@ -2,12 +2,11 @@
 site_dir: html
 site_description: typedload documentation โ€” a python module to load untyped 
data into typed data structures
 site_author: Salvo 'LtWorf' Tomaselli <[email protected]>
-copyright: Copyright (C) 2018-2024 Salvo "LtWorf" Tomaselli
+copyright: Copyright (C) 2018-2025 Salvo "LtWorf" Tomaselli
 theme: readthedocs
 use_directory_urls: false
 nav:
     - Home: README.md
-    #- Origin story: origin_story.md
     - Changelog: CHANGELOG.md
     - Using typedload:
         - Examples: examples.md
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/typedload/pyproject.toml new/typedload/pyproject.toml
--- old/typedload/pyproject.toml        2025-10-21 09:06:49.000000000 +0200
+++ new/typedload/pyproject.toml        2026-02-11 09:35:47.000000000 +0100
@@ -1,15 +1,16 @@
 [project]
 name = "typedload"
-version = "2.39"
+version = "2.40"
 authors = [
   { name="Salvo 'LtWorf' Tomaselli", email="[email protected]" },
 ]
 description = "Load and dump data from json-like format into typed data 
structures"
 readme = "README.md"
 requires-python = ">=3.10"
-classifiers = ['Development Status :: 5 - Production/Stable', 'Intended 
Audience :: Developers', 'License :: OSI Approved :: GNU General Public License 
v3 (GPLv3)', 'Typing :: Typed', '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']
+classifiers = ['Development Status :: 5 - Production/Stable', 'Intended 
Audience :: Developers', 'Typing :: Typed', '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']
 keywords = ['typing', 'types', 'mypy', 'json', 'schema', 'json-schema', 
'python3', 'namedtuple', 'enums', 'dataclass', 'pydantic']
-license = {file = "LICENSE"}
+license = "GPL-3.0-only"
+license-files = ["LICENSE"]
 
 [project.urls]
 "Homepage" = "https://ltworf.codeberg.page/typedload/";
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/typedload/setup.py new/typedload/setup.py
--- old/typedload/setup.py      2025-10-21 09:06:49.000000000 +0200
+++ new/typedload/setup.py      2026-02-11 09:35:47.000000000 +0100
@@ -3,14 +3,14 @@
 from setuptools import setup
 setup(
     name='typedload',
-    version='2.39',
+    version='2.40',
     description='Load and dump data from json-like format into typed data 
structures',
     readme='README.md',
     url='https://ltworf.codeberg.page/typedload/',
     author="Salvo 'LtWorf' Tomaselli",
     author_email='[email protected]',
     license='GPL-3.0-only',
-    classifiers=['Development Status :: 5 - Production/Stable', 'Intended 
Audience :: Developers', 'License :: OSI Approved :: GNU General Public License 
v3 (GPLv3)', 'Typing :: Typed', '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'],
+    classifiers=['Development Status :: 5 - Production/Stable', 'Intended 
Audience :: Developers', 'Typing :: Typed', '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'],
     keywords='typing types mypy json schema json-schema python3 namedtuple 
enums dataclass pydantic',
     packages=['typedload'],
     package_data={"typedload": ["py.typed", "__init__.pyi"]},
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/typedload/tests/__main__.py 
new/typedload/tests/__main__.py
--- old/typedload/tests/__main__.py     2025-10-21 08:51:45.000000000 +0200
+++ new/typedload/tests/__main__.py     2026-02-11 09:33:45.000000000 +0100
@@ -1,5 +1,5 @@
 # typedload
-# Copyright (C) 2018-2025 Salvo "LtWorf" Tomaselli
+# Copyright (C) 2018-2026 Salvo "LtWorf" Tomaselli
 #
 # typedload is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -35,7 +35,7 @@
 from .test_datetime import *
 from .test_literal import *
 from .test_typeddict import *
-from .test_orunion import *
+from .test_moretypes import *
 
 if sys.version_info.minor >= 12:
     from .test_typealias import *
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/typedload/tests/test_dataloader.py 
new/typedload/tests/test_dataloader.py
--- old/typedload/tests/test_dataloader.py      2025-10-21 08:51:45.000000000 
+0200
+++ new/typedload/tests/test_dataloader.py      2025-11-29 12:08:43.000000000 
+0100
@@ -182,6 +182,12 @@
         with self.assertRaises(TypeError):
             loader.load({'a': 1}, Union[A, B])
 
+    def test_loadnewunion(self):
+        t = list[int] | str
+        assert load('ciao', t) == 'ciao'
+        assert load(['1', 1.0, 0], t) == [1, 1, 0]
+        assert load(('1', 1.0, 0), t) == [1, 1, 0]
+
 
 class TestFastIterableLoad(unittest.TestCase):
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/typedload/tests/test_moretypes.py 
new/typedload/tests/test_moretypes.py
--- old/typedload/tests/test_moretypes.py       1970-01-01 01:00:00.000000000 
+0100
+++ new/typedload/tests/test_moretypes.py       2026-02-11 09:33:45.000000000 
+0100
@@ -0,0 +1,70 @@
+# typedload
+# Copyright (C) 2026 Salvo "LtWorf" Tomaselli
+#
+# typedload is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# author Salvo "LtWorf" Tomaselli <[email protected]>
+
+import unittest
+
+from typedload.moretypes import *
+from typedload import load, dump
+
+
+class TestHexRGB(unittest.TestCase):
+
+    def test_dump(self):
+        assert dump(HexRGB('#Ff0000')) == '#FF0000'
+        assert isinstance(dump(HexRGB('#Ff0000')), str)
+
+    def test_load(self):
+        assert load('#0000ff', HexRGB) == HexRGB('#0000Ff')
+        assert isinstance(load('#0000ff', HexRGB), HexRGB)
+
+    def test_values(self):
+        assert HexRGB('#Ff0000').rgb() == (255, 0, 0)
+        assert HexRGB('#0AFFFf').rgb() == (10, 255, 255)
+        assert HexRGB('#000000').rgb() == (0, 0, 0)
+        assert HexRGB('#000001').rgb() == (0, 0, 1)
+        assert HexRGB(7).rgb() == (0, 0, 7)
+
+    def test_equality(self):
+        assert HexRGB('#0000ff') ==  HexRGB('#0000FF')
+        assert HexRGB('#0000ff') !=  '#0000ff'
+        assert HexRGB('#0000ff') !=  255
+        assert HexRGB('#0000ff').value ==  255
+
+    def test_fail(self):
+        with self.assertRaises(TypeError):
+            HexRGB()
+        with self.assertRaises(ValueError):
+            HexRGB('#ffaab')
+        with self.assertRaises(ValueError):
+            HexRGB('FFAABB')
+        with self.assertRaises(ValueError):
+            HexRGB('#00000x')
+        with self.assertRaises(ValueError):
+            HexRGB(-1)
+        with self.assertRaises(ValueError):
+            HexRGB(16777216)
+        with self.assertRaises(TypeError):
+            HexRGB(4.1)
+
+    def test_immutable(self):
+        c = HexRGB(1)
+
+        with self.assertRaises(AttributeError):
+            c.qwe = 123
+        with self.assertRaises(AttributeError):
+            c.value = 2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/typedload/tests/test_orunion.py 
new/typedload/tests/test_orunion.py
--- old/typedload/tests/test_orunion.py 2025-10-21 08:51:45.000000000 +0200
+++ new/typedload/tests/test_orunion.py 1970-01-01 01:00:00.000000000 +0100
@@ -1,48 +0,0 @@
-# typedload
-# Copyright (C) 2022 Salvo "LtWorf" Tomaselli
-#
-# typedload is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-# author Salvo "LtWorf" Tomaselli <[email protected]>
-
-
-import unittest
-
-from typedload import dataloader, load, dump, typechecks
-
-
-class TestOrUnion(unittest.TestCase):
-    '''
-    From Python3.10 unions can be written as A | B.
-
-    That is a completely different internal than Union[A, B]
-    '''
-
-    def test_typechecker(self):
-        assert typechecks.is_union(int | str)
-        assert not typechecks.is_union(2 | 1)
-
-    def test_uniontypes(self):
-        u = int | str | float
-        assert int in typechecks.uniontypes(u)
-        assert str in typechecks.uniontypes(u)
-        assert float in typechecks.uniontypes(u)
-        assert bytes not in typechecks.uniontypes(u)
-        assert bool not in typechecks.uniontypes(u)
-
-    def test_loadnewunion(self):
-        t = list[int] | str
-        assert load('ciao', t) == 'ciao'
-        assert load(['1', 1.0, 0], t) == [1, 1, 0]
-        assert load(('1', 1.0, 0), t) == [1, 1, 0]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/typedload/tests/test_typechecks.py 
new/typedload/tests/test_typechecks.py
--- old/typedload/tests/test_typechecks.py      2025-10-21 08:51:45.000000000 
+0200
+++ new/typedload/tests/test_typechecks.py      2025-11-29 12:08:43.000000000 
+0100
@@ -1,5 +1,5 @@
 # typedload
-# Copyright (C) 2018-2024 Salvo "LtWorf" Tomaselli
+# Copyright (C) 2018-2025 Salvo "LtWorf" Tomaselli
 #
 # typedload is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -156,14 +156,28 @@
         assert typechecks.is_union(Union[str, int, float])
         assert not typechecks.is_union(FrozenSet[int])
         assert not typechecks.is_union(int)
+        # New syntax
+        assert typechecks.is_union(int | None)
+        assert typechecks.is_union(str | None)
+        assert typechecks.is_union(bytes | str)
+        assert typechecks.is_union(str | int | float)
+        assert typechecks.is_union(int | str)
+        assert not typechecks.is_union(2 | 1)
 
     def test_is_optional(self):
+        T = int | str
+        assert typechecks.is_optional(Optional[T])
         assert typechecks.is_optional(Optional[int])
         assert typechecks.is_optional(Optional[str])
         assert not typechecks.is_optional(Union[bytes, str])
         assert not typechecks.is_optional(Union[str, int, float])
         assert not typechecks.is_union(FrozenSet[int])
         assert not typechecks.is_union(int)
+        # New syntax
+        assert typechecks.is_optional(None | int)
+        assert typechecks.is_optional(None | str)
+        assert not typechecks.is_optional(bytes | str)
+        assert not typechecks.is_optional(str | int | float)
 
     def test_is_nonetype(self):
         assert typechecks.is_nonetype(type(None))
@@ -201,6 +215,14 @@
         with self.assertRaises(AttributeError):
             typechecks.uniontypes(Union[int])
 
+        u = int | str | float
+        assert int in typechecks.uniontypes(u)
+        assert str in typechecks.uniontypes(u)
+        assert float in typechecks.uniontypes(u)
+        assert bytes not in typechecks.uniontypes(u)
+        assert bool not in typechecks.uniontypes(u)
+
+
     def test_any(self):
         assert typechecks.is_any(Any)
         assert not typechecks.is_any(str)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/typedload/typedload/__init__.py 
new/typedload/typedload/__init__.py
--- old/typedload/typedload/__init__.py 2025-10-21 08:51:45.000000000 +0200
+++ new/typedload/typedload/__init__.py 2026-02-11 09:33:45.000000000 +0100
@@ -156,7 +156,7 @@
 to the loader/dumper.
 """
 
-# Copyright (C) 2018-2021 Salvo "LtWorf" Tomaselli
+# Copyright (C) 2018-2026 Salvo "LtWorf" Tomaselli
 #
 # typedload is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -183,6 +183,7 @@
     'datadumper',
     'dump',
     'typechecks',
+    'moretypes',
 ]
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/typedload/typedload/datadumper.py 
new/typedload/typedload/datadumper.py
--- old/typedload/typedload/datadumper.py       2025-10-21 08:51:45.000000000 
+0200
+++ new/typedload/typedload/datadumper.py       2026-02-11 09:33:45.000000000 
+0100
@@ -3,7 +3,7 @@
 This module is the inverse of dataloader. It converts typed
 data structures to things that json can serialize.
 """
-# Copyright (C) 2018-2024 Salvo "LtWorf" Tomaselli
+# Copyright (C) 2018-2026 Salvo "LtWorf" Tomaselli
 #
 # typedload is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -32,6 +32,7 @@
 
 from .exceptions import TypedloadValueError
 from .typechecks import is_attrs, NONETYPE, is_literal
+from . import moretypes
 
 
 __all__ = [
@@ -138,6 +139,7 @@
                 ipaddress.IPv4Interface,
                 ipaddress.IPv6Interface,
                 uuid.UUID,
+                moretypes.HexRGB,
             }
 
         self.handlers = [
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/typedload/typedload/dataloader.py 
new/typedload/typedload/dataloader.py
--- old/typedload/typedload/dataloader.py       2025-10-21 08:51:45.000000000 
+0200
+++ new/typedload/typedload/dataloader.py       2026-02-11 09:33:45.000000000 
+0100
@@ -3,7 +3,7 @@
 Module to load data into typed data structures
 """
 
-# Copyright (C) 2018-2025 Salvo "LtWorf" Tomaselli
+# Copyright (C) 2018-2026 Salvo "LtWorf" Tomaselli
 #
 # typedload is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -34,6 +34,7 @@
 from .typechecks import *
 from .typechecks import discriminatorliterals
 from .helpers import tname
+from . import moretypes
 
 try:
     # A dirty trick
@@ -222,6 +223,7 @@
             ipaddress.IPv4Interface,
             ipaddress.IPv6Interface,
             uuid.UUID,
+            moretypes.HexRGB,
         }
 
         # Bah
@@ -862,11 +864,14 @@
             return type_(l.load(value, t, 
annotation=Annotation(AnnotationType.UNION, t)))
         except Exception as e:
             exceptions.append(e)
+
+    # For small enums of basic types, list the possible values
     if len(type_.__members__) <= 10 and all(type(i.value) in l.basictypes for 
i in type_.__members__.values()):
-        lst = '\nValue %s not between: ' % repr(value) + \
+        lst = f'\nValue {value!r} not among: ' + \
         ', '.join(repr(i.value) for i in type_.__members__.values())
     else:
         lst = ''
+
     raise TypedloadValueError(
         'Value of %s could not be loaded into %s%s' % (tname(type(value)), 
tname(type_), lst),
         value=value,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/typedload/typedload/moretypes.py 
new/typedload/typedload/moretypes.py
--- old/typedload/typedload/moretypes.py        1970-01-01 01:00:00.000000000 
+0100
+++ new/typedload/typedload/moretypes.py        2026-02-11 09:33:45.000000000 
+0100
@@ -0,0 +1,91 @@
+"""
+typedload
+
+Additional types
+"""
+
+# Copyright (C) 2026 Salvo "LtWorf" Tomaselli
+#
+# typedload is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# author Salvo "LtWorf" Tomaselli <[email protected]>
+
+__all__ = [
+    'HexRGB'
+]
+
+
+class HexRGB:
+    '''
+    A class for RGB colours.
+
+    Can be initialised with:
+    HexRGB('#00ff00')
+    HexRGB(1234)
+    '''
+
+    __slots__ = ['value']
+    value: int
+
+    def __new__(cls, value: str | int):
+        if isinstance(value, str):
+            if len(value) != 7:
+                raise ValueError(f'Invalid length for HexRGB string: 
{value!r}')
+            if value[0] != '#':
+                raise ValueError(f'Invalid prefix for HexRGB string: 
{value[0]!r}')
+            v = int(value[1:], 16)
+        elif isinstance(value, int):
+            v = value
+        else:
+            raise TypeError(f'Invalid type {type(value)} to create HexRGB')
+
+        if v < 0 or v > 16777215:
+            raise ValueError(f'Colour value out of range: {v}')
+
+        # Some weird magic since this class is immutable
+        instance = object.__new__(cls)
+        object.__setattr__(instance, "value", v)
+        return instance
+
+    def __int__(self) -> int:
+        return self.value
+
+    def __eq__(self, o) -> bool:
+        if not isinstance(o, HexRGB):
+            return False
+        return self.value == o.value
+
+    def __hash__(self):
+        return hash(self.value)
+
+    def __setattr__(self, name, value) -> None:
+        raise AttributeError('HexRGB is read only')
+
+    def __repr__(self) -> str:
+        return f"HexRGB({self.value})"
+
+    def __str__(self) -> str:
+        return f'#{self.value:06X}'
+
+    def rgb(self) -> tuple[int, int, int]:
+        '''
+        Returns a tuple with the RGB values in integer format
+        '''
+        v = self.value
+        b = v & 255
+        v >>= 8
+        g = v & 255
+        v >>= 8
+        r = v
+        return r, g, b
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/typedload/typedload/typechecks.py 
new/typedload/typedload/typechecks.py
--- old/typedload/typedload/typechecks.py       2025-10-21 08:51:45.000000000 
+0200
+++ new/typedload/typedload/typechecks.py       2026-02-10 23:39:12.000000000 
+0100
@@ -15,7 +15,7 @@
 different versions of Python.
 """
 
-# Copyright (C) 2019-2024 Salvo "LtWorf" Tomaselli
+# Copyright (C) 2019-2026 Salvo "LtWorf" Tomaselli
 #
 # typedload is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -34,7 +34,8 @@
 import sys
 from enum import Enum
 from re import Pattern
-from typing import Any, Tuple, Union, Set, List, Dict, Type, FrozenSet, NewType
+from typing import Any, Tuple, Set, List, Dict, Type, FrozenSet, NewType
+from typing import Union #TODO useless after python 3.13
 
 
 __all__ = [
@@ -119,7 +120,7 @@
     Note that Optional is just a Union, so if is_optional is True then
     also is_union will be True
     '''
-    return is_union(type_) and (len(type_.__args__) == 2) and NONETYPE in 
type_.__args__
+    return is_union(type_) and NONETYPE in type_.__args__
 
 
 def is_nonetype(type_: Any) -> bool:
@@ -211,13 +212,8 @@
     return hasattr(type_, '__attrs_attrs__')
 
 
-if sys.version_info > (3, 10, 0):
-    def is_newtype(type_: Any) -> bool:
-        return type(type_) == NewType
-
-else:
-    def is_newtype(type_: Any) -> bool:
-        return hasattr(type_, '__supertype__')
+def is_newtype(type_: Any) -> bool:
+    return type(type_) == NewType
 
 
 def uniontypes(type_: Any) -> Tuple[Type[Any], ...]:

Reply via email to