pax_global_header 0000666 0000000 0000000 00000000064 15067567316 0014532 g ustar 00root root 0000000 0000000 52 comment=d9454af38d2785bea3243d5b76018e5549a5eb58
atom-0.12.1/ 0000775 0000000 0000000 00000000000 15067567316 0012553 5 ustar 00root root 0000000 0000000 atom-0.12.1/.github/ 0000775 0000000 0000000 00000000000 15067567316 0014113 5 ustar 00root root 0000000 0000000 atom-0.12.1/.github/FUNDING.yml 0000664 0000000 0000000 00000001215 15067567316 0015727 0 ustar 00root root 0000000 0000000 # These are supported funding model platforms
github: [MatthieuDartiailh]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
atom-0.12.1/.github/dependabot.yml 0000664 0000000 0000000 00000000242 15067567316 0016741 0 ustar 00root root 0000000 0000000 version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly" atom-0.12.1/.github/workflows/ 0000775 0000000 0000000 00000000000 15067567316 0016150 5 ustar 00root root 0000000 0000000 atom-0.12.1/.github/workflows/ci.yml 0000664 0000000 0000000 00000007707 15067567316 0017301 0 ustar 00root root 0000000 0000000 name: Continuous Integration
on:
schedule:
- cron: '0 0 * * 3'
push:
branches:
- main
pull_request:
branches:
- main
paths:
- .github/workflows/ci.yml
- "atom/**"
- "tests/**"
- "examples/**"
- setup.py
- pyproject.toml
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.11']
steps:
- uses: actions/checkout@v5
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
cache-dependency-path: 'lint_requirements.txt'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- name: Install project
run: |
pip install -e .
- name: Install dependencies
run: |
pip install -U -r lint_requirements.txt
- name: Formatting
if: always()
run: |
ruff format atom examples tests --check
- name: Linting
if: always()
run: |
ruff check atom examples tests
- name: Typing
if: always()
run: |
mypy atom examples
types:
name: Type checking
runs-on: ubuntu-latest
needs:
- lint
if: needs.lint.result == 'success'
strategy:
matrix:
python-version: ['3.10']
steps:
- uses: actions/checkout@v5
- name: Get history and tags for SCM versioning to work
run: |
git fetch --prune --unshallow
git fetch --depth=1 origin +refs/tags/*:refs/tags/*
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- name: Install project
run: |
pip install .
- name: Run Mypy
run: |
pip install mypy pytest
mypy atom
- name: Test with pytest
run: |
pip install pytest-mypy-plugins regex
python -X dev -m pytest tests/type_checking -v
tests:
name: Unit tests
runs-on: ${{ matrix.os }}
needs:
- lint
if: needs.lint.result == 'success'
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14-dev']
steps:
- uses: actions/checkout@v5
- name: Get history and tags for SCM versioning to work
run: |
git fetch --prune --unshallow
git fetch --depth=1 origin +refs/tags/*:refs/tags/*
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip setuptools
pip install setuptools-scm[toml]
pip install git+https://github.com/nucleic/cppy@main
- name: Install project
env:
CFLAGS: --coverage
# Use legcay command to install the library to be able to measure
# coverage of C++ code.
run: |
python setup.py develop
- name: Test with pytest
run: |
pip install -r test_requirements.txt
python -X dev -m pytest tests --ignore=tests/type_checking --cov --cov-report xml -v -W error
# - name: Generate C++ coverage reports
# if: (github.event_name != 'schedule' && matrix.os != 'windows-latest')
# run: |
# bash -c "find . -type f -name '*.gcno' -exec gcov -pb --all-blocks {} +" || true
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: unittests
name: codecov-umbrella
fail_ci_if_error: true
verbose: true
atom-0.12.1/.github/workflows/docs.yml 0000664 0000000 0000000 00000002243 15067567316 0017624 0 ustar 00root root 0000000 0000000 name: Documentation building
on:
schedule:
- cron: '0 0 * * 2'
push:
branches:
- main
pull_request:
branches:
- main
paths:
- .github/workflows/docs.yml
- "atom/**"
- "docs/**"
- "examples/**"
- setup.py
- pyproject.toml
jobs:
docs:
name: Docs building
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Get history and tags for SCM versioning to work
run: |
git fetch --prune --unshallow
git fetch --depth=1 origin +refs/tags/*:refs/tags/*
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r docs/requirements.txt
- name: Install project
run: |
pip install .
- name: Install graphviz
uses: ts-graphviz/setup-graphviz@v1
- name: Build documentation
# Remove -W till the multiple reference can be fixed in Sphinx 4
run: |
mkdir docs_output;
sphinx-build docs/source docs_output -b html;
atom-0.12.1/.github/workflows/release.yml 0000664 0000000 0000000 00000007164 15067567316 0020323 0 ustar 00root root 0000000 0000000 name: Build and upload wheels
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * 3'
push:
tags:
- '*'
jobs:
build_sdist:
name: Build sdist
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Get history and tags for SCM versioning to work
run: |
git fetch --prune --unshallow
git fetch --depth=1 origin +refs/tags/*:refs/tags/*
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: '3.x'
- name: Build sdist
run: |
pip install --upgrade pip
pip install wheel build
python -m build . -s
- name: Test sdist
run: |
pip install pytest
pip install dist/*.tar.gz
cd ..
python -m pytest atom/tests
- name: Store artifacts
uses: actions/upload-artifact@v4
with:
name: cibw-sdist
path: dist/*
build_wheels:
name: Build wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Get history and tags for SCM versioning to work
run: |
git fetch --prune --unshallow
git fetch --depth=1 origin +refs/tags/*:refs/tags/*
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: '3.x'
- name: Install cibuildwheel
run: |
python -m pip install --upgrade pip
python -m pip install wheel cibuildwheel
- name: Build wheels
env:
CIBW_BUILD: "cp310-* cp311-* cp312-* cp313-* cp314-*"
CIBW_ARCHS_MACOS: x86_64 universal2 arm64
CIBW_ARCHS_WINDOWS: auto64
CIBW_TEST_REQUIRES: pytest
CIBW_TEST_COMMAND: python -m pytest {package}/tests -v
run: |
python -m cibuildwheel . --output-dir dist
- name: Store artifacts
uses: actions/upload-artifact@v4
with:
name: cibw-wheels-${{ runner.os }}
path: dist/*.whl
publish:
if: github.event_name == 'push'
needs: [build_wheels, build_sdist]
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/atom
permissions:
id-token: write
steps:
- name: Download all the dists
uses: actions/download-artifact@v5.0.0
with:
pattern: cibw-*
path: dist
merge-multiple: true
- uses: pypa/gh-action-pypi-publish@release/v1
github-release:
name: >-
Sign the Python 🐍 distribution 📦 with Sigstore
and create a GitHub Release
runs-on: ubuntu-latest
needs:
- publish
permissions:
contents: write
id-token: write
steps:
- name: Download all the dists
uses: actions/download-artifact@v5.0.0
with:
pattern: cibw-*
path: dist
merge-multiple: true
- name: Sign the dists with Sigstore
uses: sigstore/gh-action-sigstore-python@v3.0.1
with:
inputs: >-
./dist/*.tar.gz
./dist/*.whl
- name: Create GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
run: >-
gh release create
'${{ github.ref_name }}'
--repo '${{ github.repository }}'
--generate-notes
- name: Upload artifact signatures to GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
run: >-
gh release upload
'${{ github.ref_name }}' dist/**
--repo '${{ github.repository }}'
atom-0.12.1/.gitignore 0000664 0000000 0000000 00000004017 15067567316 0014545 0 ustar 00root root 0000000 0000000 # pycharm project files
.idea
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
atom/version.py
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
reports/*
!reports/pycallgraph
!reports/pycallgraph/*
htmlcov/
cov_html/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# VS Code
.vscode
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# Data files
*.csv
# HTML files
*.html
# VSCodeCounter cache files
.VSCodeCounter
# Profiling folder used by pytest-profile
prof/
atom-0.12.1/.pre-commit-config.yaml 0000664 0000000 0000000 00000000463 15067567316 0017037 0 ustar 00root root 0000000 0000000 repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.1.6
hooks:
# Run the linter.
- id: ruff
# Run the formatter.
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.7.0
hooks:
- id: mypy
additional_dependencies: [] atom-0.12.1/.readthedocs.yaml 0000664 0000000 0000000 00000001176 15067567316 0016007 0 ustar 00root root 0000000 0000000 # .readthedocs.yml# .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Set the version of Python and other tools you might need
build:
os: ubuntu-20.04
tools:
python: "3.10"
apt_packages:
- graphviz
# Build documentation in the docs/source directory with Sphinx
sphinx:
configuration: docs/source/conf.py
# Enable epub output
formats:
- epub
# Optionally declare the Python requirements required to build your docs
python:
install:
- requirements: docs/requirements.txt
- method: pip
path: .
atom-0.12.1/LICENSE 0000664 0000000 0000000 00000006601 15067567316 0013563 0 ustar 00root root 0000000 0000000 =========================
The Atom licensing terms
=========================
Atom is licensed under the terms of the Modified BSD License (also known as
New or Revised BSD), as follows:
Copyright (c) 2013-2025, Nucleic Development Team
Copyright (c) 2013, Enthought, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
Neither the name of the Nucleic Development Team nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
About Atom
----------
Chris Colbert began the Atom project under the umbrella of Enthought, Inc.
in February 2013. The project was forked into the Nucleic project in March
2013. Chris is still the project lead.
The Nucleic Development Team is the set of all contributors to the Nucleic
project and its subprojects.
The core team that coordinates development on GitHub can be found here:
http://github.com/nucleic. The current team consists of:
Core Developers:
* Chris Colbert
* Matthieu Dartiailh
Active Contributors:
* Steven Silvester
* Dave Willmer
Our Copyright Policy
--------------------
Nucleic uses a shared copyright model. Each contributor maintains copyright
over their contributions to Nucleic. But, it is important to note that these
contributions are typically only changes to the repositories. Thus, the Nucleic
source code, in its entirety is not the copyright of any single person or
institution. Instead, it is the collective copyright of the entire Nucleic
Development Team. If individual contributors want to maintain a record of what
changes/contributions they have specific copyright on, they should indicate
their copyright in the commit message of the change, when they commit the
change to one of the Nucleic repositories.
With this in mind, the following banner should be used in any source code file
to indicate the copyright and license terms:
#------------------------------------------------------------------------------
# Copyright (c) 2013-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
#------------------------------------------------------------------------------
atom-0.12.1/MANIFEST.in 0000664 0000000 0000000 00000001025 15067567316 0014307 0 ustar 00root root 0000000 0000000 include MANIFEST.in
include *.txt
include *.rst
include *.md
include Makefile
include LICENSE
recursive-include docs/source *.rst
recursive-include docs/source *.png
recursive-include docs/source *.py
include docs/make.bat
include docs/Makefile
recursive-include examples *.py
recursive-include licenses *.txt
include atom/py.typed
recursive-include atom *.py
recursive-include atom *.pyi
recursive-include atom/src *.cpp
recursive-include atom/src *.h
recursive-include tests *.py
prune .git
prune docs/build
prune dist
prune build
atom-0.12.1/README.rst 0000664 0000000 0000000 00000005562 15067567316 0014252 0 ustar 00root root 0000000 0000000 Welcome to Atom
===============
.. image:: https://github.com/nucleic/atom/workflows/Continuous%20Integration/badge.svg
:target: https://github.com/nucleic/atom/actions
.. image:: https://github.com/nucleic/atom/workflows/Documentation%20building/badge.svg
:target: https://github.com/nucleic/atom/actions
.. image:: https://codecov.io/gh/nucleic/atom/branch/main/graph/badge.svg
:target: https://codecov.io/gh/nucleic/atom
.. image:: https://readthedocs.org/projects/atom/badge/?version=latest
:target: https://atom.readthedocs.io/en/latest/?badge=latest
:alt: Documentation Status
.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
:target: https://github.com/astral-sh/ruff
:alt: Ruff
Atom is a framework for creating memory efficient Python objects with enhanced
features such as dynamic initialization, validation, and change notification for
object attributes. It provides the default model binding behavior for the
`Enaml `_ UI framework.
Examples:
.. code-block:: python
from atom.api import Atom, Str, Range, Bool, observe
class Person(Atom):
""" A simple class representing a person object.
"""
last_name = Str()
first_name = Str()
age = Range(low=0)
debug = Bool(False)
@observe('age')
def debug_print(self, change):
""" Prints out a debug message whenever the person's age changes.
"""
if self.debug:
templ = "{first} {last} is {age} years old."
s = templ.format(
first=self.first_name, last=self.last_name, age=self.age,
)
print(s)
def _default_first_name(self):
return 'John'
john = Person(last_name='Doe', age=42)
john.debug = True
john.age = 43 # prints message
john.age = 'forty three' # raises TypeError
Starting with atom 0.8.0 atom object can also be defined using type annotations.
.. code-block:: python
from atom.api import Atom, observe
class InventoryItem(Atom):
"""Class for keeping track of an item in inventory."""
name: str
unit_price: float
quantity_on_hand: int = 0
def total_cost(self) -> float:
return self.unit_price * self.quantity_on_hand
@observe("unit_price")
def check_for_price_reduction(self, change):
savings = change.get("oldvalue", 0) - change.get("value")
if savings > 0:
print(f"Save ${savings} now on {self.name}s!")
>>> w = InventoryItem(name="widget", unit_price=1.99, quantity_on_hand=10)
>>> w.unit_price = 1.00
Save $0.99 now on widgets!
For version information, see `the Revision History `_.
atom-0.12.1/atom/ 0000775 0000000 0000000 00000000000 15067567316 0013513 5 ustar 00root root 0000000 0000000 atom-0.12.1/atom/__init__.py 0000664 0000000 0000000 00000000561 15067567316 0015626 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2013-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
atom-0.12.1/atom/api.py 0000664 0000000 0000000 00000004623 15067567316 0014643 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2013-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
"""Module exporting the public interface to atom."""
from .atom import Atom
from .catom import (
CAtom,
ChangeType,
DefaultValue,
GetAttr,
GetState,
Member,
PostGetAttr,
PostSetAttr,
PostValidate,
SetAttr,
Validate,
atomclist,
atomdict,
atomlist,
atomref,
atomset,
defaultatomdict,
)
from .coerced import Coerced
from .containerlist import ContainerList
from .delegator import Delegator
from .dict import DefaultDict, Dict
from .enum import Enum
from .event import Event
from .instance import ForwardInstance, Instance
from .list import List
from .meta import (
AtomMeta,
MissingMemberWarning,
add_member,
clone_if_needed,
observe,
set_default,
)
from .property import Property, cached_property
from .scalars import (
Bool,
Bytes,
Callable,
Constant,
Float,
FloatRange,
Int,
Range,
ReadOnly,
Str,
Value,
)
from .set import Set
from .signal import Signal
from .subclass import ForwardSubclass, Subclass
from .tuple import FixedTuple, Tuple
from .typed import ForwardTyped, Typed
from .typing_utils import ChangeDict
__all__ = [
"Atom",
"AtomMeta",
"Bool",
"Bytes",
"CAtom",
"Callable",
"ChangeDict",
"ChangeType",
"Coerced",
"Constant",
"ContainerList",
"DefaultDict",
"DefaultValue",
"Delegator",
"Dict",
"Enum",
"Event",
"FixedTuple",
"Float",
"FloatRange",
"ForwardInstance",
"ForwardSubclass",
"ForwardTyped",
"GetAttr",
"GetState",
"Instance",
"Int",
"List",
"Member",
"MissingMemberWarning",
"PostGetAttr",
"PostSetAttr",
"PostValidate",
"Property",
"Range",
"ReadOnly",
"Set",
"SetAttr",
"Signal",
"Str",
"Subclass",
"Tuple",
"Typed",
"Validate",
"Value",
"add_member",
"atomclist",
"atomdict",
"atomlist",
"atomref",
"atomset",
"cached_property",
"clone_if_needed",
"defaultatomdict",
"observe",
"set_default",
]
atom-0.12.1/atom/atom.py 0000664 0000000 0000000 00000005544 15067567316 0015035 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2013-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from contextlib import contextmanager
from typing import ClassVar, Iterator, Mapping
from .catom import CAtom, Member
from .meta import AtomMeta, add_member, observe, set_default # noqa
# The above are imported to avoid breaking code relying on internals
def __newobj__(cls, *args):
"""A compatibility pickler function.
This function is not part of the public Atom api.
"""
return cls.__new__(cls, *args)
class Atom(CAtom, metaclass=AtomMeta):
"""The base class for defining atom objects.
`Atom` objects are special Python objects which never allocate an
instance dictionary unless one is explicitly requested. The storage
for an atom is instead computed from the `Member` objects declared
on the class. Memory is reserved for these members with no over
allocation.
This restriction make atom objects a bit less flexible than normal
Python objects, but they are between 3x-10x more memory efficient
than normal objects depending on the number of attributes.
"""
__atom_members__: ClassVar[Mapping[str, Member]]
@classmethod
def members(cls) -> Mapping[str, Member]:
"""Get the members dictionary for the type.
Returns
-------
result : Mapping[str, Member]
The dictionary of members defined on the class. User code
should not modify the contents of the dict.
"""
return cls.__atom_members__
@contextmanager
def suppress_notifications(self) -> Iterator[None]:
"""Disable member notifications within in a context.
Returns
-------
result : contextmanager
A context manager which disables atom notifications for the
duration of the context. When the context exits, the state
is restored to its previous value.
"""
old = self.set_notifications_enabled(False)
yield
self.set_notifications_enabled(old)
def __reduce_ex__(self, proto):
"""An implementation of the reduce protocol.
This method creates a reduction tuple for Atom instances. This
method should not be overridden by subclasses unless the author
fully understands the rammifications.
"""
args = (type(self), *self.__getnewargs__())
return (__newobj__, args, self.__getstate__())
def __getnewargs__(self):
"""Get the argument tuple to pass to __new__ on unpickling.
See the Python.org docs for more information.
"""
return ()
atom-0.12.1/atom/catom.pyi 0000664 0000000 0000000 00000041106 15067567316 0015343 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2021-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from enum import IntEnum, IntFlag
from typing import (
Any,
Callable,
Dict,
Generic,
List,
Literal,
Optional,
Sequence,
Set,
Tuple,
Type,
TypeVar,
overload,
)
from typing_extensions import Self
from .atom import Atom
from .property import Property
from .typing_utils import ChangeDict
class ChangeType(IntFlag):
CREATE = ...
UPDATE = ...
DELETE = ...
EVENT = ...
PROPERTY = ...
CONTAINER = ...
ANY = ...
def reset_property(prop: Property[Any, Any], owner: Atom) -> None: ...
class CAtom:
def __init__(self, **kwargs: Any) -> None: ...
def freeze(self) -> None: ...
def get_member(self, member: str) -> Member[Any, Any]: ...
def has_observer(
self, member: str, func: Callable[[Dict[str, Any]], None]
) -> bool: ...
def has_observers(self, member: str) -> bool: ...
def notifications_enabled(self) -> bool: ...
def notify(self, member_name: str, *args: Any, **kwargs: Any) -> None: ...
def observe(
self,
member: str,
func: Callable[[ChangeDict], None],
change_types: ChangeType = ChangeType.ANY,
) -> None: ...
def set_notifications_enabled(self, enabled: bool) -> bool: ...
def unobserve(self, member: str, func: Callable[[ChangeDict], None]) -> None: ...
def __sizeof__(self) -> int: ...
T = TypeVar("T")
S = TypeVar("S")
class Member(Generic[T, S]):
index: int = ...
metadata: Optional[Dict[str, Any]] = ...
name: str = ...
# Mode accessors
default_value_mode: Tuple[DefaultValue, Any] = ...
delattr_mode: Tuple[DelAttr, Any] = ...
getattr_mode: Tuple[GetAttr, Any] = ...
post_getattr_mode: Tuple[PostGetAttr, Any] = ...
post_setattr_mode: Tuple[PostSetAttr, Any] = ...
post_validate_mode: Tuple[PostValidate, Any] = ...
setattr_mode: Tuple[SetAttr, Any] = ...
validate_mode: Tuple[Validate, Any] = ...
getstate_mode: Tuple[GetState, Any] = ...
def __init__(self) -> None: ...
@overload
def __get__(self, instance: None, owner: Type[Atom]) -> Self: ...
@overload
def __get__(self, instance: Atom, owner: Type[Atom]) -> T: ...
def __set__(self, instance: Atom, value: S) -> None: ...
def __delete__(self, instance: Atom) -> None: ...
def tag(self, **kwargs: Any) -> Self: ...
def clone(self) -> Self: ...
def add_static_observer(
self,
observer: str | Callable[[ChangeDict], None],
change_types: ChangeType = ChangeType.ANY,
) -> Any: ...
def remove_static_observer(
self, observer: str | Callable[[ChangeDict], None]
) -> Any: ...
def static_observers(self) -> Tuple[str | Callable[[ChangeDict], None], ...]: ...
def has_observer(
self,
observer: str | Callable[[ChangeDict], None],
change_types: ChangeType = ChangeType.ANY,
) -> Any: ...
def has_observers(self, change_types: ChangeType = ChangeType.ANY) -> bool: ...
def copy_static_observers(self, other: Member[Any, Any]) -> None: ...
def notify(self, owner: CAtom, *args: Any, **kwargs: Any) -> None: ...
def get_slot(self, owner: CAtom) -> Any: ...
def set_slot(self, owner: CAtom, value: Any) -> None: ...
def del_slot(self, owner: CAtom) -> None: ...
# Manual operation of the member
def do_default_value(self, owner: CAtom) -> Any: ...
def do_delattr(self, owner: CAtom) -> None: ...
def do_full_validate(self, owner: CAtom, old: T, new: Any) -> T: ...
def do_getattr(self, owner: CAtom) -> Any: ...
def do_post_getattr(self, owner: CAtom, value: Any) -> T: ...
def do_post_setattr(self, owner: CAtom, old: T, new: T) -> None: ...
def do_post_validate(self, owner: CAtom, old: T, new: T) -> T: ...
def do_setattr(self, owner: CAtom, value: Any) -> Any: ...
def do_validate(self, owner: CAtom, old: T, new: Any) -> T: ...
def do_should_getstate(self, owner: CAtom) -> bool: ...
# Setter for the member
def set_index(self, index: int) -> None: ...
def set_name(self, name: str) -> None: ...
# Default value mode
@overload
def set_default_value_mode(
self, mode: Literal[DefaultValue.List], context: list[Any]
) -> None: ...
@overload
def set_default_value_mode(
self, mode: Literal[DefaultValue.Set], context: set[Any]
) -> None: ...
@overload
def set_default_value_mode(
self, mode: Literal[DefaultValue.Dict], context: dict[Any, Any]
) -> None: ...
@overload
def set_default_value_mode(
self, mode: Literal[DefaultValue.Delegate], context: Member[T, S]
) -> None: ...
@overload
def set_default_value_mode(
self, mode: Literal[DefaultValue.CallObject], context: Callable[[], Any]
) -> None: ...
@overload
def set_default_value_mode(
self,
mode: Literal[DefaultValue.CallObject_Object],
context: Callable[[CAtom], Any],
) -> None: ...
@overload
def set_default_value_mode(
self,
mode: Literal[DefaultValue.CallObject_ObjectName],
context: Callable[[CAtom, str], Any],
) -> None: ...
@overload
def set_default_value_mode(
self,
mode: Literal[DefaultValue.ObjectMethod]
| Literal[DefaultValue.ObjectMethod_Name]
| Literal[DefaultValue.MemberMethod_Object],
context: str,
) -> None: ...
@overload
def set_default_value_mode(
self, mode: Literal[DefaultValue.Static], context: Any
) -> None: ...
@overload
def set_default_value_mode(
self, mode: Literal[DefaultValue.NonOptional], context: None
) -> None: ...
@overload
def set_default_value_mode(
self, mode: Literal[DefaultValue.NoOp], context: None
) -> None: ...
# Delattr mode
@overload
def set_delattr_mode(
self, mode: Literal[DelAttr.Delegate], context: Member[T, S]
) -> None: ...
@overload
def set_delattr_mode(
self,
mode: Literal[DelAttr.Property],
context: Optional[Callable[[CAtom], None]],
) -> None: ...
@overload
def set_delattr_mode(
self,
mode: Literal[DelAttr.Constant]
| Literal[DelAttr.Event]
| Literal[DelAttr.NoOp]
| Literal[DelAttr.ReadOnly]
| Literal[DelAttr.Signal]
| Literal[DelAttr.Slot],
context: None,
) -> None: ...
# Getattr mode
@overload
def set_getattr_mode(
self, mode: Literal[GetAttr.Delegate], context: Member[T, S]
) -> None: ...
@overload
def set_getattr_mode(
self,
mode: Literal[GetAttr.Property] | Literal[GetAttr.CachedProperty],
context: Optional[Callable[[CAtom], Any]],
) -> None: ...
@overload
def set_getattr_mode(
self, mode: Literal[GetAttr.CallObject_Object], context: Callable[[CAtom], Any]
) -> None: ...
@overload
def set_getattr_mode(
self,
mode: Literal[GetAttr.CallObject_ObjectName],
context: Callable[[CAtom, str], Any],
) -> None: ...
@overload
def set_getattr_mode(
self,
mode: Literal[GetAttr.ObjectMethod]
| Literal[GetAttr.ObjectMethod_Name]
| Literal[GetAttr.MemberMethod_Object],
context: str,
) -> None: ...
@overload
def set_getattr_mode(
self,
mode: Literal[GetAttr.Event]
| Literal[GetAttr.NoOp]
| Literal[GetAttr.Signal]
| Literal[GetAttr.Slot],
context: str,
) -> None: ...
# Post getattr mode
@overload
def set_post_getattr_mode(
self, mode: Literal[PostGetAttr.NoOp], context: None
) -> None: ...
@overload
def set_post_getattr_mode(
self, mode: Literal[PostGetAttr.Delegate], context: Member[T, S]
) -> None: ...
@overload
def set_post_getattr_mode(
self,
mode: Literal[PostGetAttr.ObjectMethod_Value]
| Literal[PostGetAttr.ObjectMethod_NameValue]
| Literal[PostGetAttr.MemberMethod_ObjectValue],
context: str,
) -> None: ...
# Post setattr mode
@overload
def set_post_setattr_mode(
self, mode: Literal[PostSetAttr.NoOp], context: None
) -> None: ...
@overload
def set_post_setattr_mode(
self, mode: Literal[PostSetAttr.Delegate], context: Member[T, S]
) -> None: ...
@overload
def set_post_setattr_mode(
self,
mode: Literal[PostSetAttr.ObjectMethod_OldNew]
| Literal[PostSetAttr.ObjectMethod_NameOldNew]
| Literal[PostSetAttr.MemberMethod_ObjectOldNew],
context: str,
) -> None: ...
# Post validate mode
@overload
def set_post_validate_mode(
self, mode: Literal[PostValidate.NoOp], context: None
) -> None: ...
@overload
def set_post_validate_mode(
self, mode: Literal[PostValidate.Delegate], context: Member[T, S]
) -> None: ...
@overload
def set_post_validate_mode(
self,
mode: Literal[PostValidate.ObjectMethod_OldNew]
| Literal[PostValidate.ObjectMethod_NameOldNew]
| Literal[PostValidate.MemberMethod_ObjectOldNew],
context: str,
) -> None: ...
# Setattr mode
@overload
def set_setattr_mode(
self,
mode: Literal[SetAttr.Constant]
| Literal[SetAttr.Event]
| Literal[SetAttr.NoOp]
| Literal[SetAttr.ReadOnly]
| Literal[SetAttr.Signal]
| Literal[SetAttr.Slot],
context: None,
) -> None: ...
@overload
def set_setattr_mode(
self, mode: Literal[SetAttr.Delegate], context: Member[T, S]
) -> None: ...
@overload
def set_setattr_mode(
self,
mode: Literal[SetAttr.Property],
context: Optional[Callable[[CAtom], Any]],
) -> None: ...
@overload
def set_setattr_mode(
self,
mode: Literal[SetAttr.CallObject_ObjectValue],
context: Callable[[CAtom, Any], Any],
) -> None: ...
@overload
def set_setattr_mode(
self,
mode: Literal[SetAttr.CallObject_ObjectNameValue],
context: Callable[[CAtom, str, Any], Any],
) -> None: ...
@overload
def set_setattr_mode(
self,
mode: Literal[SetAttr.ObjectMethod_Value]
| Literal[SetAttr.ObjectMethod_NameValue]
| Literal[SetAttr.MemberMethod_ObjectValue],
context: str,
) -> None: ...
# Validate mode
@overload
def set_validate_mode(
self,
mode: Literal[Validate.Bool]
| Literal[Validate.Bytes]
| Literal[Validate.BytesPromote]
| Literal[Validate.Callable]
| Literal[Validate.Float]
| Literal[Validate.FloatPromote]
| Literal[Validate.Int]
| Literal[Validate.IntPromote]
| Literal[Validate.NoOp]
| Literal[Validate.Str]
| Literal[Validate.StrPromote],
context: None,
) -> None: ...
@overload
def set_validate_mode(
self,
mode: Literal[Validate.Tuple]
| Literal[Validate.List]
| Literal[Validate.ContainerList]
| Literal[Validate.Set],
context: Optional[Member[Any, Any]],
) -> None: ...
@overload
def set_validate_mode(
self,
mode: Literal[Validate.Dict],
context: Tuple[Optional[Member[Any, Any]], Optional[Member[Any, Any]]],
) -> None: ...
@overload
def set_validate_mode(
self,
mode: Literal[Validate.Instance]
| Literal[Validate.OptionalInstance]
| Literal[Validate.Subclass],
context: type | Tuple[type, ...],
) -> None: ...
@overload
def set_validate_mode(
self,
mode: Literal[Validate.Typed] | Literal[Validate.OptionalTyped],
context: type,
) -> None: ...
@overload
def set_validate_mode(
self, mode: Literal[Validate.Enum], context: Sequence[Any]
) -> None: ...
@overload
def set_validate_mode(
self,
mode: Literal[Validate.FloatRange],
context: Tuple[Optional[float], Optional[float]],
) -> None: ...
@overload
def set_validate_mode(
self,
mode: Literal[Validate.Range],
context: Tuple[Optional[int], Optional[int]],
) -> None: ...
@overload
def set_validate_mode(
self,
mode: Literal[Validate.Coerced],
context: Tuple[Type[T], Callable[[Any], T]],
) -> None: ...
@overload
def set_validate_mode(
self, mode: Literal[Validate.Delegate], context: Member[T, S]
) -> None: ...
@overload
def set_validate_mode(
self,
mode: Literal[Validate.ObjectMethod_OldNew]
| Literal[Validate.ObjectMethod_NameOldNew]
| Literal[Validate.MemberMethod_ObjectOldNew],
context: str,
) -> None: ...
# Getstate mode
@overload
def set_getstate_mode(
self,
mode: Literal[GetState.Exclude]
| Literal[GetState.Include]
| Literal[GetState.IncludeNonDefault]
| Literal[GetState.Property],
context: None,
) -> None: ...
@overload
def set_getstate_mode(
self,
mode: Literal[GetState.MemberMethod_Object]
| Literal[GetState.ObjectMethod_Name],
context: str,
) -> None: ...
KT = TypeVar("KT")
VT = TypeVar("VT")
class atomlist(List[T]): ...
class atomclist(atomlist[T]): ...
class atomset(Set[T]): ...
class atomdict(Dict[KT, VT]): ...
class defaultatomdict(Dict[KT, VT]): ...
A = TypeVar("A", bound=CAtom)
class atomref(Generic[A]):
def __new__(cls, atom: A) -> atomref[A]: ...
def __bool__(self) -> bool: ...
def __call__(self) -> Optional[A]: ...
def __sizeof__(self) -> int: ...
class SignalConnector:
def __call__(self, *args: Any, **kwargs: Any) -> None: ...
def emit(self, *args: Any, **kwargs: Any) -> None: ...
def connect(self, slot: Callable[..., Any]) -> None: ...
def disconnect(self, slot: Callable[..., Any]) -> None: ...
class EventBinder:
def bind(self, observer: Callable[[Dict[str, Any]], None]) -> None: ...
def unbind(self, observer: Callable[[Dict[str, Any]], None]) -> None: ...
class DefaultValue(IntEnum):
CallObject = ...
CallObject_Object = ...
CallObject_ObjectName = ...
Delegate = ...
Dict = ...
DefaultDict = ...
List = ...
MemberMethod_Object = ...
NonOptional = ...
NoOp = ...
ObjectMethod = ...
ObjectMethod_Name = ...
Set = ...
Static = ...
class DelAttr(IntEnum):
Constant = ...
Delegate = ...
Event = ...
NoOp = ...
Property = ...
ReadOnly = ...
Signal = ...
Slot = ...
class GetAttr(IntEnum):
CachedProperty = ...
CallObject_Object = ...
CallObject_ObjectName = ...
Delegate = ...
Event = ...
MemberMethod_Object = ...
NoOp = ...
ObjectMethod = ...
ObjectMethod_Name = ...
Property = ...
Signal = ...
Slot = ...
class PostGetAttr(IntEnum):
Delegate = ...
MemberMethod_ObjectValue = ...
NoOp = ...
ObjectMethod_NameValue = ...
ObjectMethod_Value = ...
class PostSetAttr(IntEnum):
Delegate = ...
MemberMethod_ObjectOldNew = ...
NoOp = ...
ObjectMethod_NameOldNew = ...
ObjectMethod_OldNew = ...
class PostValidate(IntEnum):
Delegate = ...
MemberMethod_ObjectOldNew = ...
NoOp = ...
ObjectMethod_NameOldNew = ...
ObjectMethod_OldNew = ...
class SetAttr(IntEnum):
CallObject_ObjectNameValue = ...
CallObject_ObjectValue = ...
Constant = ...
Delegate = ...
Event = ...
MemberMethod_ObjectValue = ...
NoOp = ...
ObjectMethod_NameValue = ...
ObjectMethod_Value = ...
Property = ...
ReadOnly = ...
Signal = ...
Slot = ...
class Validate(IntEnum):
Bool = ...
Bytes = ...
BytesPromote = ...
Callable = ...
Coerced = ...
ContainerList = ...
Delegate = ...
Dict = ...
DefaultDict = ...
Enum = ...
FixedTuple = ...
Float = ...
FloatPromote = ...
FloatRange = ...
Instance = ...
Int = ...
IntPromote = ...
List = ...
MemberMethod_ObjectOldNew = ...
OptionalInstance = ...
OptionalTyped = ...
NoOp = ...
ObjectMethod_NameOldNew = ...
ObjectMethod_OldNew = ...
Range = ...
Set = ...
Str = ...
StrPromote = ...
Subclass = ...
Tuple = ...
Typed = ...
class GetState(IntEnum):
Exclude = ...
Include = ...
IncludeNonDefault = ...
Property = ...
MemberMethod_Object = ...
ObjectMethod_Name = ...
atom-0.12.1/atom/coerced.py 0000664 0000000 0000000 00000005243 15067567316 0015475 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2013-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from .catom import DefaultValue, Member, Validate
from .typing_utils import extract_types, is_optional
class Coerced(Member):
"""A member which will coerce a value to a given instance type.
Unlike Typed or Instance, a Coerced value is not intended to be
set to None.
"""
__slots__ = ()
def __init__(self, kind, args=None, kwargs=None, *, factory=None, coercer=None):
"""Initialize a Coerced.
Parameters
----------
kind : type or tuple of types
The allowable types for the value.
args : tuple, optional
If 'factory' is None, then 'kind' is a callable type and
these arguments will be passed to the constructor to create
the default value.
kwargs : dict, optional
If 'factory' is None, then 'kind' is a callable type and
these keywords will be passed to the constructor to create
the default value.
factory : callable, optional
An optional callable which takes no arguments and returns
the default value for the member. If this is not provided
then 'args' and 'kwargs' should be provided, as 'kind' will
be used to generate the default value.
coercer : callable, optional
An optional callable which takes the value and returns the
coerced value. If this is not given, then 'kind' must be a
callable type which will be called with the value to coerce
the value to the appropriate type.
"""
origin = kind
kind = extract_types(kind)
opt, temp = is_optional(kind)
if factory is not None:
self.set_default_value_mode(DefaultValue.CallObject, factory)
else:
args = args or ()
kwargs = kwargs or {}
if opt:
def factory():
return None
else:
def factory():
return kind[0](*args, **kwargs)
self.set_default_value_mode(DefaultValue.CallObject, factory)
if not coercer and (isinstance(origin, tuple) or len(temp) > 1):
raise ValueError(f"No coercer was provided but {origin} is not callable.")
self.set_validate_mode(Validate.Coerced, (kind, coercer or temp[0]))
atom-0.12.1/atom/coerced.pyi 0000664 0000000 0000000 00000012075 15067567316 0015647 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2021-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from typing import Any, Callable, Dict, Optional, Tuple, Type, TypeVar, overload
from .catom import Member
T = TypeVar("T")
T1 = TypeVar("T1")
T2 = TypeVar("T2")
S = TypeVar("S")
class Coerced(Member[T, S]):
# No default
# - type
@overload
def __new__(
cls,
kind: Type[T],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: None = None,
coercer: None = None,
) -> Coerced[T, T]: ...
@overload
def __new__(
cls,
kind: Type[T],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: None = None,
coercer: Callable[[S], T],
) -> Coerced[T, T | S]: ...
# - 1-Tuple[Any, ...]
@overload
def __new__(
cls,
kind: Tuple[Type[T]],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: None = None,
coercer: None = None,
) -> Coerced[T, T]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T]],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: None = None,
coercer: Callable[[S], T],
) -> Coerced[T, T | S]: ...
# - 2-Tuple[Any, ...]
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1]],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: None = None,
coercer: None = None,
) -> Coerced[T | T1, T | T1]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1]],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: None = None,
coercer: Callable[[S], T | T1],
) -> Coerced[T | T1, T | T1 | S]: ...
# - 3-Tuple[Any, ...]
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1], Type[T2]],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: None = None,
coercer: None = None,
) -> Coerced[T | T1 | T2, T | T1 | T2]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1], Type[T2]],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: None = None,
coercer: Callable[[S], T | T1 | T2],
) -> Coerced[T | T1 | T2, T | T1 | T2 | S]: ...
# Default with factory
# - type
@overload
def __new__(
cls,
kind: Type[T],
args: None = None,
kwargs: None = None,
*,
factory: Callable[[], T],
coercer: None = None,
) -> Coerced[T, T]: ...
@overload
def __new__(
cls,
kind: Type[T],
args: None = None,
kwargs: None = None,
*,
factory: Callable[[], T | S],
coercer: Callable[[S], T],
) -> Coerced[T, T | S]: ...
# - 1-Tuple[Any, ...]
@overload
def __new__(
cls,
kind: Tuple[Type[T]],
args: None = None,
kwargs: None = None,
*,
factory: Callable[[], T],
coercer: None = None,
) -> Coerced[T, T]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T]],
args: None = None,
kwargs: None = None,
*,
factory: Callable[[], T | S],
coercer: Callable[[S], T],
) -> Coerced[T, T | S]: ...
# - 2-Tuple[Any, ...]
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1]],
args: None = None,
kwargs: None = None,
*,
factory: Callable[[], T | T1],
coercer: None = None,
) -> Coerced[T | T1, T | T1]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1]],
args: None = None,
kwargs: None = None,
*,
factory: Callable[[], T | T1 | S],
coercer: Callable[[S], T | T1],
) -> Coerced[T | T1, T | T1 | S]: ...
# - 3-Tuple[Any, ...]
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1], Type[T2]],
args: None = None,
kwargs: None = None,
*,
factory: Callable[[], T | T1 | T2],
coercer: None = None,
) -> Coerced[T | T1 | T2, T | T1 | T2]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1], Type[T2]],
args: None = None,
kwargs: None = None,
*,
factory: Callable[[], S],
coercer: Callable[[S], T | T1 | T2 | S],
) -> Coerced[T | T1 | T2, T | T1 | T2 | S]: ...
atom-0.12.1/atom/containerlist.py 0000664 0000000 0000000 00000001356 15067567316 0016750 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2013-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from .catom import Validate
from .list import List
class ContainerList(List):
"""A List member which supports container notifications."""
__slots__ = ()
def __init__(self, item=None, default=None):
"""Initialize a ContainerList."""
super(ContainerList, self).__init__(item, default)
self.set_validate_mode(Validate.ContainerList, self.item)
atom-0.12.1/atom/containerlist.pyi 0000664 0000000 0000000 00000004706 15067567316 0017123 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2021-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from typing import Any, List, Optional, Tuple, Type, TypeVar, overload
from .catom import Member
T = TypeVar("T")
T1 = TypeVar("T1")
T2 = TypeVar("T2")
class ContainerList(Member[List[T], List[T]]):
# No default
@overload
def __new__(
cls, kind: None = None, default: Optional[List[Any]] = None
) -> ContainerList[Any]: ...
@overload
def __new__(cls, kind: Type[T], default: None = None) -> ContainerList[T]: ...
@overload
def __new__(
cls, kind: Tuple[Type[T]], default: None = None
) -> ContainerList[T]: ...
@overload
def __new__(
cls, kind: Tuple[Type[T], Type[T1]], default: None = None
) -> ContainerList[T | T1]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1], Type[T2]],
default: None = None,
) -> ContainerList[T | T1 | T2]: ...
@overload
def __new__(
cls, kind: Member[T, Any], default: None = None
) -> ContainerList[T]: ...
# With default
@overload
def __new__(cls, kind: Type[T], default: List[T]) -> ContainerList[T]: ...
@overload
def __new__(cls, kind: Tuple[Type[T]], default: List[T]) -> ContainerList[T]: ...
@overload
def __new__(
cls, kind: Tuple[Type[T], Type[T1]], default: List[T | T1]
) -> ContainerList[T | T1]: ...
@overload
def __new__(
cls, kind: Tuple[Type[T], Type[T1]], default: List[T] | List[T1]
) -> ContainerList[T | T1]: ...
@overload
def __new__(
cls, kind: Tuple[Type[T], Type[T1], Type[T2]], default: List[T | T1 | T2]
) -> ContainerList[T | T1 | T2]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1], Type[T2]],
default: List[T | T1] | List[T | T2] | List[T1 | T2],
) -> ContainerList[T | T1 | T2]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1], Type[T2]],
default: List[T] | List[T1] | List[T2],
) -> ContainerList[T | T1 | T2]: ...
@overload
def __new__(
cls, kind: Member[T, Any], default: Optional[List[T]] = None
) -> ContainerList[T]: ...
atom-0.12.1/atom/datastructures/ 0000775 0000000 0000000 00000000000 15067567316 0016570 5 ustar 00root root 0000000 0000000 atom-0.12.1/atom/datastructures/__init__.py 0000664 0000000 0000000 00000000561 15067567316 0020703 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2013-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
atom-0.12.1/atom/datastructures/api.py 0000664 0000000 0000000 00000000653 15067567316 0017717 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2013-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from .sortedmap import sortedmap
__all__ = ["sortedmap"]
atom-0.12.1/atom/datastructures/sortedmap.pyi 0000664 0000000 0000000 00000002351 15067567316 0021312 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2021-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from typing import Generic, List, Optional, Tuple, TypeVar, Union, overload
K = TypeVar("K")
V = TypeVar("V")
D = TypeVar("D")
class sortedmap(Generic[K, V]):
@overload
def get(self, key: K, default: None = None) -> Optional[V]: ...
@overload
def get(self, key: K, default: D) -> Union[V, D]: ...
@overload
def pop(self, key: K, default: None = None) -> V: ...
@overload
def pop(self, key: K, default: D) -> Union[V, D]: ...
def clear(self) -> None: ...
def keys(self) -> List[K]: ...
def values(self) -> List[V]: ...
def items(self) -> List[Tuple[K, V]]: ...
def copy(self) -> "sortedmap[K, V]": ...
def __contains__(self, key: K) -> bool: ...
def __getitem__(self, key: K) -> V: ...
def __setitem__(self, key: K, value: V) -> None: ...
def __delitem__(self, key: K) -> None: ...
def __sizeof__(self) -> int: ...
atom-0.12.1/atom/delegator.py 0000664 0000000 0000000 00000011470 15067567316 0016036 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2013-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from .catom import (
DefaultValue,
Member,
PostGetAttr,
PostSetAttr,
PostValidate,
Validate,
)
class Delegator(Member):
"""A member subclass which delegates all work to a wrapped member.
The only behaviors not delegated are GetAttr and SetAttr. Subclasses
should override behavior as needed to suit their needs. In order to
change a particular mode, the relevant change method must be called
via super(Delegator, ...).
"""
__slots__ = "delegate"
def __init__(self, delegate):
"""Initialize a DeclarativeProperty.
Parameters
----------
delegate : Member
The Atom Member which provides the behavior for the property.
The member should use standard slot behavior semantics.
"""
self.delegate = delegate
sup = super(Delegator, self)
sup.set_post_getattr_mode(PostGetAttr.Delegate, delegate)
sup.set_post_setattr_mode(PostSetAttr.Delegate, delegate)
sup.set_default_value_mode(DefaultValue.Delegate, delegate)
sup.set_validate_mode(Validate.Delegate, delegate)
sup.set_post_validate_mode(PostValidate.Delegate, delegate)
def add_static_observer(self, observer):
"""Add a static observer to the member.
This method also adds the static observer to the delegate.
"""
super(Delegator, self).add_static_observer(observer)
self.delegate.add_static_observer(observer)
def remove_static_observer(self, observer):
"""Remove a static observer from the member.
This method also removes the static observer from the delegate.
"""
super(Delegator, self).remove_static_observer(observer)
self.delegate.remove_static_observer(observer)
def set_name(self, name):
"""Assign the name to this member.
This method keeps the name of the delegate member in sync.
"""
super(Delegator, self).set_name(name)
self.delegate.set_name(name)
def set_index(self, index):
"""Assign the index to this member.
This method keeps the index of the delegate member in sync.
"""
super(Delegator, self).set_index(index)
self.delegate.set_index(index)
def set_post_getattr_mode(self, mode, context):
"""Set the post getattr mode for the member.
This method proxies the change to the delegate member.
"""
self.delegate.set_post_getattr_mode(mode, context)
def set_post_setattr_mode(self, mode, context):
"""Set the post getattr mode for the member.
This method proxies the change to the delegate member.
"""
self.delegate.set_post_setattr_mode(mode, context)
def set_default_value_mode(self, mode, context):
"""Set the default value mode for the member.
This method proxies the change to the delegate member.
"""
self.delegate.set_default_value_mode(mode, context)
def set_validate_mode(self, mode, context):
"""Set the default value mode for the member.
This method proxies the change to the delegate member.
"""
self.delegate.set_validate_mode(mode, context)
def set_post_validate_mode(self, mode, context):
"""Set the default value mode for the member.
This method proxies the change to the delegate member.
"""
self.delegate.set_post_validate_mode(mode, context)
def clone(self):
"""Create a clone of the declarative property.
This method also creates a clone of the internal delegate for
mode handlers which use the original delegate as the context.
"""
clone = super(Delegator, self).clone()
delegate = self.delegate
clone.delegate = delegate_clone = delegate.clone()
mode, old = clone.post_getattr_mode
if old is delegate:
clone.set_post_getattr_mode(mode, delegate_clone)
mode, old = clone.post_setattr_mode
if old is delegate:
clone.set_post_setattr_mode(mode, delegate_clone)
mode, old = clone.default_value_mode
if old is delegate:
clone.set_default_value_mode(mode, delegate_clone)
mode, old = clone.validate_mode
if old is delegate:
clone.set_validate_mode(mode, delegate_clone)
mode, old = clone.post_validate_mode
if old is delegate:
clone.set_post_validate_mode(mode, delegate_clone)
return clone
atom-0.12.1/atom/delegator.pyi 0000664 0000000 0000000 00000001055 15067567316 0016205 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2021-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from typing import TypeVar
from .catom import Member
T = TypeVar("T")
S = TypeVar("S")
class Delegator(Member[T, S]):
def __new__(cls, member: Member[T, S]) -> Delegator[T, S]: ...
atom-0.12.1/atom/dict.py 0000664 0000000 0000000 00000020427 15067567316 0015015 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2013-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from collections import defaultdict
from .catom import DefaultValue, Member, Validate
from .instance import Instance
from .typing_utils import extract_types, is_optional
class Dict(Member):
"""A value of type `dict`."""
__slots__ = ()
def __init__(self, key=None, value=None, default=None):
"""Initialize a Dict.
Parameters
----------
key : Member, type, tuple of types, or None, optional
A member to use for validating the types of keys allowed in
the dict. This can also be a type or a tuple of types, which
will be wrapped with an Instance member. If this is not
given, no key validation is performed.
value : Member, type, tuple of types, or None, optional
A member to use for validating the types of values allowed
in the dict. This can also be a type or a tuple of types,
which will be wrapped with an Instance member. If this is
not given, no value validation is performed.
default : dict, optional
The default dict of items. A new copy of this dict will be
created for each atom instance.
"""
self.set_default_value_mode(DefaultValue.Dict, default)
if key is not None and not isinstance(key, Member):
opt, types = is_optional(extract_types(key))
key = Instance(types, optional=opt)
if value is not None and not isinstance(value, Member):
opt, types = is_optional(extract_types(value))
value = Instance(types, optional=opt)
self.set_validate_mode(Validate.Dict, (key, value))
def set_name(self, name):
"""Assign the name to this member.
This method is called by the Atom metaclass when a class is
created. This makes sure the name of the internal members are
also updated.
"""
super(Dict, self).set_name(name)
key, value = self.validate_mode[1]
if key is not None:
key.set_name(name + "|key")
if value is not None:
value.set_name(name + "|value")
def set_index(self, index):
"""Assign the index to this member.
This method is called by the Atom metaclass when a class is
created. This makes sure the index of the internal members are
also updated.
"""
super(Dict, self).set_index(index)
key, value = self.validate_mode[1]
if key is not None:
key.set_index(index)
if value is not None:
value.set_index(index)
def clone(self):
"""Create a clone of the member.
This will clone the internal dict key and value members if they exist.
"""
clone = super(Dict, self).clone()
key, value = self.validate_mode[1]
if key is not None or value is not None:
key_clone = key.clone() if key is not None else None
value_clone = value.clone() if value is not None else None
mode, _ = self.validate_mode
clone.set_validate_mode(mode, (key_clone, value_clone))
return clone
class _DefaultWrapper:
__slots__ = ("wrapped",)
def __init__(self, wrapped):
self.wrapped = wrapped
def __call__(self, atom):
return self.wrapped()
def __repr__(self):
return repr(self.wrapped)
class DefaultDict(Member):
"""A value of type `dict` implementing __missing__"""
__slots__ = ()
def __init__(self, key=None, value=None, default=None, *, missing=None):
"""Initialize a DefaultDict.
Parameters
----------
key : Member, type, tuple of types, or None, optional
A member to use for validating the types of keys allowed in
the dict. This can also be a type or a tuple of types, which
will be wrapped with an Instance member. If this is not
given, no key validation is performed.
value : Member, type, tuple of types, or None, optional
A member to use for validating the types of values allowed
in the dict. This can also be a type or a tuple of types,
which will be wrapped with an Instance member. If this is
not given, no value validation is performed.
default : dict or None, optional
The default dict of items. A new copy of this dict will be
created for each atom instance.
missing : Callable[[], Any] or None, optional
Factory to build a default value for a missing key in the dictionary.
"""
self.set_default_value_mode(DefaultValue.DefaultDict, default)
if key is not None and not isinstance(key, Member):
opt, types = is_optional(extract_types(key))
key = Instance(types, optional=opt)
if value is not None and not isinstance(value, Member):
opt, types = is_optional(extract_types(value))
# Assume a default value can be created to avoid the need to specify a
# missing factory in simple case even for custom types.
value = Instance(types, optional=opt, args=())
if missing is not None:
if not callable(missing):
raise ValueError(
f"The missing argument expect a callable, got {missing}"
)
try:
missing()
except Exception as e:
raise ValueError(
"The missing argument expect a callable taking no argument. "
"Trying to call it with not argument failed with the chained "
"exception."
) from e
missing = _DefaultWrapper(missing)
if isinstance(default, defaultdict):
if missing is not None:
raise ValueError(
"Both a missing factory and a default value which is a default "
"dictionary were specified. When using a default dict as default "
"value missing should be omitted."
)
missing = _DefaultWrapper(default.default_factory)
if (
missing is None
and value is not None
and value.default_value_mode[0]
not in (DefaultValue.NoOp, DefaultValue.NonOptional)
):
missing = value.do_default_value
if missing is None:
raise ValueError(
"No missing value factory was specified and none could be "
"deduced from the value member."
)
self.set_validate_mode(Validate.DefaultDict, (key, value, missing))
def set_name(self, name):
"""Assign the name to this member.
This method is called by the Atom metaclass when a class is
created. This makes sure the name of the internal members are
also updated.
"""
super().set_name(name)
key, value, _ = self.validate_mode[1]
if key is not None:
key.set_name(name + "|key")
if value is not None:
value.set_name(name + "|value")
def set_index(self, index):
"""Assign the index to this member.
This method is called by the Atom metaclass when a class is
created. This makes sure the index of the internal members are
also updated.
"""
super().set_index(index)
key, value, _ = self.validate_mode[1]
if key is not None:
key.set_index(index)
if value is not None:
value.set_index(index)
def clone(self):
"""Create a clone of the member.
This will clone the internal dict key and value members if they exist.
"""
clone = super().clone()
mode, (key, value, missing) = self.validate_mode
key_clone = key.clone() if key is not None else None
value_clone = value.clone() if value is not None else None
clone.set_validate_mode(mode, (key_clone, value_clone, missing))
return clone
atom-0.12.1/atom/dict.pyi 0000664 0000000 0000000 00000046602 15067567316 0015171 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2021-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from typing import (
Any,
Callable,
DefaultDict as TDefaultDict,
Dict as TDict,
Optional,
Tuple,
Type,
TypeVar,
overload,
)
from .catom import Member
T = TypeVar("T")
KT = TypeVar("KT")
VT = TypeVar("VT")
KT1 = TypeVar("KT1")
VT1 = TypeVar("VT1")
KT2 = TypeVar("KT2")
VT2 = TypeVar("VT2")
class Dict(Member[TDict[KT, VT], TDict[KT, VT]]):
# Untyped
@overload
def __new__(
cls,
key: None = None,
value: None = None,
default: Optional[TDict[Any, Any]] = None,
) -> Dict[Any, Any]: ...
# No default
# Typed keys
# - type
@overload
def __new__(
cls,
key: Type[KT],
value: None = None,
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT, Any]: ...
# - 1-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT]],
value: None = None,
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT, Any]: ...
# - 2-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1]],
value: None = None,
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT | KT1, Any]: ...
# - 3-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1], Type[KT2]],
value: None = None,
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT | KT1 | KT2, Any]: ...
# - member
@overload
def __new__(
cls,
key: Member[KT, Any],
value: None = None,
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT, Any]: ...
# Typed values
# - type
@overload
def __new__(
cls, key: None, value: Type[VT], default: Optional[TDict[Any, Any]] = None
) -> Dict[Any, VT]: ...
# - 1-tuple
@overload
def __new__(
cls,
key: None,
value: Tuple[Type[VT]],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[Any, VT]: ...
# - 2-tuple
@overload
def __new__(
cls,
key: None,
value: Tuple[Type[VT], Type[VT1]],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[Any, VT | VT1]: ...
# - 3-tuple
@overload
def __new__(
cls,
key: None,
value: Tuple[Type[VT], Type[VT1], Type[VT2]],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[Any, VT | VT1 | VT2]: ...
# - member
@overload
def __new__(
cls,
key: None,
value: Member[VT, Any],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[Any, VT]: ...
# Typed value through keyword
# - type
@overload
def __new__(
cls,
key: None = None,
*,
value: Type[VT],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[Any, VT]: ...
# - 1-tuple
@overload
def __new__(
cls,
key: None = None,
*,
value: Tuple[Type[VT]],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[Any, VT]: ...
# - 2-tuple
@overload
def __new__(
cls,
key: None = None,
*,
value: Tuple[Type[VT], Type[VT1]],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[Any, VT | VT1]: ...
# - 3-tuple
@overload
def __new__(
cls,
key: None = None,
*,
value: Tuple[Type[VT], Type[VT1], Type[VT2]],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[Any, VT | VT1 | VT2]: ...
# - member
@overload
def __new__(
cls,
key: None = None,
*,
value: Member[VT, Any],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[Any, VT]: ...
# Typed key and value
# - value simple type
# - key type
@overload
def __new__(
cls, key: Type[KT], value: Type[VT], default: Optional[TDict[Any, Any]] = None
) -> Dict[KT, VT]: ...
# - key 1-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT]],
value: Type[VT],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT, VT]: ...
# - key 2-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1]],
value: Type[VT],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT | KT1, VT]: ...
# - key 3-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1], Type[KT2]],
value: Type[VT],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT | KT1 | KT2, VT]: ...
# - key member
@overload
def __new__(
cls,
key: Member[KT, Any],
value: Type[VT],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT, VT]: ...
# - Value as single element tuple
# - key type
@overload
def __new__(
cls,
key: Type[KT],
value: Tuple[Type[VT]],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT, VT]: ...
# - key 1-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT]],
value: Tuple[Type[VT]],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT, VT]: ...
# - key 2-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1]],
value: Tuple[Type[VT]],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT | KT1, VT]: ...
# - key 3-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1], Type[KT2]],
value: Tuple[Type[VT]],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT | KT1 | KT2, VT]: ...
# - key member
@overload
def __new__(
cls,
key: Member[KT, Any],
value: Tuple[Type[VT]],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT, VT]: ...
# - Value as 2-tuple
# - key type
@overload
def __new__(
cls,
key: Type[KT],
value: Tuple[Type[VT], Type[VT1]],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT, VT | VT1]: ...
# - key 1-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT]],
value: Tuple[Type[VT], Type[VT1]],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT, VT | VT1]: ...
# - key 2-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1]],
value: Tuple[Type[VT], Type[VT1]],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT | KT1, VT | VT1]: ...
# - key 3-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1], Type[KT2]],
value: Tuple[Type[VT], Type[VT1]],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT | KT1 | KT2, VT | VT1]: ...
# - key member
@overload
def __new__(
cls,
key: Member[KT, Any],
value: Tuple[Type[VT], Type[VT1]],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT, VT | VT1]: ...
# - Value as 3-tuple
# - key type
@overload
def __new__(
cls,
key: Type[KT],
value: Tuple[Type[VT], Type[VT1], Type[VT2]],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT, VT | VT1 | VT2]: ...
# - key 1-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT]],
value: Tuple[Type[VT], Type[VT1], Type[VT2]],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT, VT | VT1 | VT2]: ...
# - key 2-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1]],
value: Tuple[Type[VT], Type[VT1], Type[VT2]],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT | KT1, VT | VT1 | VT2]: ...
# - key 3-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1], Type[KT2]],
value: Tuple[Type[VT], Type[VT1], Type[VT2]],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT | KT1 | KT2, VT | VT1 | VT2]: ...
# - key member
@overload
def __new__(
cls,
key: Member[KT, Any],
value: Tuple[Type[VT], Type[VT1], Type[VT2]],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT, VT | VT1 | VT2]: ...
# - value as member
# - key type
@overload
def __new__(
cls,
key: Type[KT],
value: Member[VT, Any],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT, VT]: ...
# - key 1-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT]],
value: Member[VT, Any],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT, VT]: ...
# - key 2-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1]],
value: Member[VT, Any],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT | KT1, VT]: ...
# - key 3-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1], Type[KT2]],
value: Member[VT, VT],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT | KT1 | KT2, VT]: ...
# - key member
@overload
def __new__(
cls,
key: Member[KT, KT],
value: Member[VT, VT],
default: Optional[TDict[Any, Any]] = None,
) -> Dict[KT, VT]: ...
class DefaultDict(Member[TDefaultDict[KT, VT], TDefaultDict[KT, VT]]):
# Untyped
@overload
def __new__(
cls,
key: None = None,
value: None = None,
default: Optional[TDict[Any, Any]] = None,
*,
missing: None = None,
) -> DefaultDict[Any, Any]: ...
# Typed by missing
@overload
def __new__(
cls,
key: None = None,
value: None = None,
default: Optional[TDict[Any, Any]] = None,
*,
missing: Callable[[], VT],
) -> DefaultDict[Any, VT]: ...
# Typed by defaultdict default value
@overload
def __new__(
cls,
key: None = None,
value: None = None,
*,
default: TDefaultDict[Any, VT],
missing: Callable[[], VT],
) -> DefaultDict[Any, VT]: ...
# No default
# Typed keys
# - type
@overload
def __new__(
cls,
key: Type[KT],
value: None = None,
default: Optional[TDict[Any, Any]] = None,
*,
missing: None = None,
) -> DefaultDict[KT, Any]: ...
# - 1-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT]],
value: None = None,
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT, Any]: ...
# - 2-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1]],
value: None = None,
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT | KT1, Any]: ...
# - 3-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1], Type[KT2]],
value: None = None,
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT | KT1 | KT2, Any]: ...
# - member
@overload
def __new__(
cls,
key: Member[KT, Any],
value: None = None,
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT, Any]: ...
# Typed values
# - type
@overload
def __new__(
cls, key: None, value: Type[VT], default: Optional[TDict[Any, Any]] = None
) -> DefaultDict[Any, VT]: ...
# - 1-tuple
@overload
def __new__(
cls,
key: None,
value: Tuple[Type[VT]],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[Any, VT]: ...
# - 2-tuple
@overload
def __new__(
cls,
key: None,
value: Tuple[Type[VT], Type[VT1]],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[Any, VT | VT1]: ...
# - 3-tuple
@overload
def __new__(
cls,
key: None,
value: Tuple[Type[VT], Type[VT1], Type[VT2]],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[Any, VT | VT1 | VT2]: ...
# - member
@overload
def __new__(
cls,
key: None,
value: Member[VT, Any],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[Any, VT]: ...
# Typed value through keyword
# - type
@overload
def __new__(
cls,
key: None = None,
*,
value: Type[VT],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[Any, VT]: ...
# - 1-tuple
@overload
def __new__(
cls,
key: None = None,
*,
value: Tuple[Type[VT]],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[Any, VT]: ...
# - 2-tuple
@overload
def __new__(
cls,
key: None = None,
*,
value: Tuple[Type[VT], Type[VT1]],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[Any, VT | VT1]: ...
# - 3-tuple
@overload
def __new__(
cls,
key: None = None,
*,
value: Tuple[Type[VT], Type[VT1], Type[VT2]],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[Any, VT | VT1 | VT2]: ...
# - member
@overload
def __new__(
cls,
key: None = None,
*,
value: Member[VT, Any],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[Any, VT]: ...
# Typed key and value
# - value simple type
# - key type
@overload
def __new__(
cls, key: Type[KT], value: Type[VT], default: Optional[TDict[Any, Any]] = None
) -> DefaultDict[KT, VT]: ...
# - key 1-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT]],
value: Type[VT],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT, VT]: ...
# - key 2-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1]],
value: Type[VT],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT | KT1, VT]: ...
# - key 3-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1], Type[KT2]],
value: Type[VT],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT | KT1 | KT2, VT]: ...
# - key member
@overload
def __new__(
cls,
key: Member[KT, Any],
value: Type[VT],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT, VT]: ...
# - Value as single element tuple
# - key type
@overload
def __new__(
cls,
key: Type[KT],
value: Tuple[Type[VT]],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT, VT]: ...
# - key 1-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT]],
value: Tuple[Type[VT]],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT, VT]: ...
# - key 2-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1]],
value: Tuple[Type[VT]],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT | KT1, VT]: ...
# - key 3-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1], Type[KT2]],
value: Tuple[Type[VT]],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT | KT1 | KT2, VT]: ...
# - key member
@overload
def __new__(
cls,
key: Member[KT, Any],
value: Tuple[Type[VT]],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT, VT]: ...
# - Value as 2-tuple
# - key type
@overload
def __new__(
cls,
key: Type[KT],
value: Tuple[Type[VT], Type[VT1]],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT, VT | VT1]: ...
# - key 1-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT]],
value: Tuple[Type[VT], Type[VT1]],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT, VT | VT1]: ...
# - key 2-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1]],
value: Tuple[Type[VT], Type[VT1]],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT | KT1, VT | VT1]: ...
# - key 3-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1], Type[KT2]],
value: Tuple[Type[VT], Type[VT1]],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT | KT1 | KT2, VT | VT1]: ...
# - key member
@overload
def __new__(
cls,
key: Member[KT, Any],
value: Tuple[Type[VT], Type[VT1]],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT, VT | VT1]: ...
# - Value as 3-tuple
# - key type
@overload
def __new__(
cls,
key: Type[KT],
value: Tuple[Type[VT], Type[VT1], Type[VT2]],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT, VT | VT1 | VT2]: ...
# - key 1-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT]],
value: Tuple[Type[VT], Type[VT1], Type[VT2]],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT, VT | VT1 | VT2]: ...
# - key 2-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1]],
value: Tuple[Type[VT], Type[VT1], Type[VT2]],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT | KT1, VT | VT1 | VT2]: ...
# - key 3-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1], Type[KT2]],
value: Tuple[Type[VT], Type[VT1], Type[VT2]],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT | KT1 | KT2, VT | VT1 | VT2]: ...
# - key member
@overload
def __new__(
cls,
key: Member[KT, Any],
value: Tuple[Type[VT], Type[VT1], Type[VT2]],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT, VT | VT1 | VT2]: ...
# - value as member
# - key type
@overload
def __new__(
cls,
key: Type[KT],
value: Member[VT, Any],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT, VT]: ...
# - key 1-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT]],
value: Member[VT, Any],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT, VT]: ...
# - key 2-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1]],
value: Member[VT, Any],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT | KT1, VT]: ...
# - key 3-tuple
@overload
def __new__(
cls,
key: Tuple[Type[KT], Type[KT1], Type[KT2]],
value: Member[VT, VT],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT | KT1 | KT2, VT]: ...
# - key member
@overload
def __new__(
cls,
key: Member[KT, KT],
value: Member[VT, VT],
default: Optional[TDict[Any, Any]] = None,
) -> DefaultDict[KT, VT]: ...
atom-0.12.1/atom/enum.py 0000664 0000000 0000000 00000005414 15067567316 0015035 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2013-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from .catom import DefaultValue, Member, Validate
class Enum(Member):
"""A member where the value can be one in a sequence of items."""
__slots__ = ()
def __init__(self, *items):
"""Initialize an Enum.
Parameters
----------
*items
The allowed values which can be assigned to the enum.
"""
if len(items) == 0:
raise ValueError("an Enum requires at least 1 item")
self.set_default_value_mode(DefaultValue.Static, items[0])
self.set_validate_mode(Validate.Enum, items)
@property
def items(self):
"""A readonly property which returns the items in the enum."""
return self.validate_mode[1]
def added(self, *items):
"""Create a clone of the Enum with added items.
Parameters
----------
*items
Additional items to include in the Enum.
Returns
-------
result : Enum
A new enum object which contains all of the original items
plus the new items.
"""
olditems = self.items
newitems = olditems + items
clone = self.clone()
clone.set_validate_mode(Validate.Enum, newitems)
return clone
def removed(self, *items):
"""Create a clone of the Enum with some items removed.
Parameters
----------
*items
The items to remove remove from the new enum.
Returns
-------
result : Enum
A new enum object which contains all of the original items
but with the given items removed.
"""
newitems = tuple(i for i in self.items if i not in items)
if len(newitems) == 0:
raise ValueError("an Enum requires at least 1 item")
clone = self.clone()
clone.set_default_value_mode(DefaultValue.Static, newitems[0])
clone.set_validate_mode(Validate.Enum, newitems)
return clone
def __call__(self, item):
"""Create a clone of the Enum item with a new default.
Parameters
----------
item : object
The item to use as the Enum default. The item must be one
of the valid enum items.
"""
if item not in self.items:
raise TypeError("invalid enum value")
clone = self.clone()
clone.set_default_value_mode(DefaultValue.Static, item)
return clone
atom-0.12.1/atom/enum.pyi 0000664 0000000 0000000 00000002010 15067567316 0015173 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2013-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from typing import Any, Tuple, TypeVar, Union, overload
from .catom import Member
# We cannot type properly an enum, as it would require some dynamic literal so this is
# just a best effort.
T = TypeVar("T")
T1 = TypeVar("T1")
class Enum(Member[T, T]):
@overload
def __new__(cls, item: T) -> Enum[T]: ... # This is hacky but should do
@overload
def __new__(cls, *items: T) -> Enum[T]: ...
@property
def items(self) -> Tuple[T, ...]: ...
def added(self: Enum[T], *items: T1) -> Enum[Union[T, T1]]: ...
def removed(self: Enum[T], *items: Any) -> Enum[T]: ...
def __call__(self: Enum[T], item: T1) -> Enum[Union[T, T1]]: ...
atom-0.12.1/atom/event.py 0000664 0000000 0000000 00000004045 15067567316 0015211 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2013-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from .catom import DelAttr, GetAttr, Member, SetAttr, Validate
from .typing_utils import extract_types
class Event(Member):
"""A member which acts like a stateless event."""
__slots__ = ()
def __init__(self, kind=None):
"""Initialize an Event.
Parameters
----------
kind : type, tuple of types or Member, optional
The type of argument which may be emitted by the event or
a Member which will validate the argument which can be
emitted. The default is None and indicates no validation
will be performed.
"""
self.set_getattr_mode(GetAttr.Event, None)
self.set_setattr_mode(SetAttr.Event, None)
self.set_delattr_mode(DelAttr.Event, None)
if kind is not None:
if isinstance(kind, Member):
self.set_validate_mode(Validate.Delegate, kind)
else:
self.set_validate_mode(Validate.Instance, extract_types(kind))
def set_name(self, name):
"""A reimplemented parent class method.
This method ensures that the delegate name is also set, if a
delegate validator is being used.
"""
super(Event, self).set_name(name)
_mode, kind = self.validate_mode
if isinstance(kind, Member):
kind.set_name(name)
def set_index(self, index):
"""A reimplemented parent class method.
This method ensures that the delegate index is also set, if a
delegate validator is being used.
"""
super(Event, self).set_index(index)
_mode, kind = self.validate_mode
if isinstance(kind, Member):
kind.set_index(index)
atom-0.12.1/atom/event.pyi 0000664 0000000 0000000 00000002057 15067567316 0015363 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2021-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from typing import Any, Tuple, Type, TypeVar, Union, overload
from .catom import EventBinder, Member
T = TypeVar("T")
T1 = TypeVar("T1")
T2 = TypeVar("T2")
class Event(Member[EventBinder, T]):
@overload
def __new__(cls, kind: None = None) -> Event[Any]: ...
@overload
def __new__(cls, kind: Type[T]) -> Event[T]: ...
@overload
def __new__(cls, kind: Tuple[Type[T]]) -> Event[T]: ...
@overload
def __new__(cls, kind: Tuple[Type[T], Type[T1]]) -> Event[Union[T, T1]]: ...
@overload
def __new__(
cls, kind: Tuple[Type[T], Type[T1], Type[T2]]
) -> Event[Union[T, T1, T2]]: ...
@overload
def __new__(cls, kind: Member[Any, T]) -> Event[T]: ...
atom-0.12.1/atom/instance.py 0000664 0000000 0000000 00000016442 15067567316 0015700 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2013-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from .catom import DefaultValue, GetState, Member, Validate
from .typing_utils import extract_types, is_optional
class Instance(Member):
"""A value which allows objects of a given type or types.
Values will be tested using the `PyObject_IsInstance` C API call.
This call is equivalent to `isinstance(value, kind)` and all the
same rules apply.
If optional is True, the value of an Instance may be set to None,
otherwise None is not considered as a valid value. By default, optional will
be considered False if a default value is provided and True otherwise.
"""
__slots__ = ()
def __init__(self, kind, args=None, kwargs=None, *, factory=None, optional=None):
"""Initialize an Instance.
Parameters
----------
kind : type or tuple of types
The allowed type or types for the instance.
args : tuple, optional
If 'factory' is None, then 'kind' is a callable type and
these arguments will be passed to the constructor to create
the default value.
kwargs : dict, optional
If 'factory' is None, then 'kind' is a callable type and
these keywords will be passed to the constructor to create
the default value.
factory : callable, optional
An optional factory to use for creating the default value.
If this is not provided and 'args' and 'kwargs' is None,
then the default value will be None, which will raised if
accessed when optional is False.
optional : bool | None, optional
Boolean indicating if None is a valid value for the member.
By default, the value is inferred to be True if no args or factory
is provided.
"""
opt, kind = is_optional(extract_types(kind))
# Since we fast track None it is relevant to identify it early.
if opt and optional is False:
raise ValueError(
"The type passed to Instance is declared optional but optional was "
"explicitly set to False"
)
# If opt is False we preserve optional as None for backward compatibility.
optional = optional if optional is not None else (opt or None)
if factory is not None:
self.set_default_value_mode(DefaultValue.CallObject, factory)
elif args is not None or kwargs is not None:
args = args or ()
kwargs = kwargs or {}
def factory():
return kind[0](*args, **kwargs)
self.set_default_value_mode(DefaultValue.CallObject, factory)
elif optional is False:
self.set_default_value_mode(DefaultValue.NonOptional, None)
optional = (
optional
if optional is not None
else (factory is None and args is None and kwargs is None)
)
if optional:
self.set_validate_mode(Validate.OptionalInstance, kind)
else:
self.set_validate_mode(Validate.Instance, kind)
# Allow to create a pickle with an unset typed value
self.set_getstate_mode(GetState.IncludeNonDefault, None)
class ForwardInstance(Instance):
"""An Instance which delays resolving the type definition.
The first time the value is accessed or modified, the type will
be resolved and the forward instance will behave identically to
a normal instance.
"""
__slots__ = ("args", "kwargs", "optional", "resolve")
def __init__(self, resolve, args=None, kwargs=None, *, factory=None, optional=None):
"""Initialize a ForwardInstance.
resolve : callable
A callable which takes no arguments and returns the type or
tuple of types to use for validating the values.
args : tuple, optional
If 'factory' is None, then 'resolve' will return a callable
type and these arguments will be passed to the constructor
to create the default value.
kwargs : dict, optional
If 'factory' is None, then 'resolve' will return a callable
type and these keywords will be passed to the constructor to
create the default value.
factory : callable, optional
An optional factory to use for creating the default value.
If this is not provided and 'args' and 'kwargs' is None,
then the default value will be None, which will raised if
accessed when optional is False.
optional : bool | None, optional
Boolean indicating if None is a valid value for the member.
By default, the value is inferred to be True if no args or factory
is provided.
"""
self.resolve = resolve
self.args = args
self.kwargs = kwargs
if factory is not None:
self.set_default_value_mode(DefaultValue.CallObject, factory)
elif args is not None or kwargs is not None:
mode = DefaultValue.MemberMethod_Object
self.set_default_value_mode(mode, "default")
elif optional is False:
self.set_default_value_mode(DefaultValue.NonOptional, None)
self.optional = (
optional
if optional is not None
else (factory is None and args is None and kwargs is None)
)
if not self.optional:
# Allow to create a pickle with an unset typed value
self.set_getstate_mode(GetState.IncludeNonDefault, None)
self.set_validate_mode(Validate.MemberMethod_ObjectOldNew, "validate")
def default(self, owner):
"""Called to retrieve the default value.
This is called the first time the default value is retrieved
for the member. It resolves the type and updates the internal
default handler to behave like a normal Instance member.
"""
kind = self.resolve()
args = self.args or ()
kwargs = self.kwargs or {}
def factory():
return kind(*args, **kwargs)
self.set_default_value_mode(DefaultValue.CallObject, factory)
return kind(*args, **kwargs)
def validate(self, owner, old, new):
"""Called to validate the value.
This is called the first time a value is validated for the
member. It resolves the type and updates the internal validate
handler to behave like a normal Instance member.
"""
kind = extract_types(self.resolve())
if self.optional:
self.set_validate_mode(Validate.OptionalInstance, kind)
else:
self.set_validate_mode(Validate.Instance, kind)
return self.do_validate(owner, old, new)
def clone(self):
"""Create a clone of the ForwardInstance object."""
clone = super(ForwardInstance, self).clone()
clone.resolve = self.resolve
clone.args = self.args
clone.kwargs = self.kwargs
clone.optional = self.optional
return clone
atom-0.12.1/atom/instance.pyi 0000664 0000000 0000000 00000036674 15067567316 0016062 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2021-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from typing import (
Any,
Callable,
Dict,
Literal,
Optional,
Tuple,
Type,
TypeVar,
overload,
)
from .catom import Member
T = TypeVar("T")
T1 = TypeVar("T1")
T2 = TypeVar("T2")
class Instance(Member[T, T]):
# Single Type
@overload
def __new__(
cls,
kind: Type[T],
args: None = None,
kwargs: None = None,
*,
factory: None = None,
optional: None = None,
) -> Instance[Optional[T]]: ...
@overload
def __new__(
cls,
kind: Type[T],
args: Tuple[Any, ...],
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: None = None,
optional: None = None,
) -> Instance[T]: ...
@overload
def __new__(
cls,
kind: Type[T],
args: None = None,
*,
kwargs: Dict[str, Any],
factory: None = None,
optional: None = None,
) -> Instance[T]: ...
@overload
def __new__(
cls,
kind: Type[T],
args: None = None,
kwargs: None = None,
*,
factory: Callable[[], T],
optional: None = None,
) -> Instance[T]: ...
@overload
def __new__(
cls,
kind: Type[T],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: Optional[Callable[[], T]] = None,
optional: Literal[True],
) -> Instance[Optional[T]]: ...
@overload
def __new__(
cls,
kind: Type[T],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: Optional[Callable[[], T]] = None,
optional: Literal[False],
) -> Instance[T]: ...
# 1-Tuple[Any, ...]
@overload
def __new__(
cls,
kind: Tuple[Type[T]],
args: None = None,
kwargs: None = None,
*,
factory: None = None,
optional: None = None,
) -> Instance[Optional[T]]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T]],
args: Tuple[Any, ...],
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: None = None,
optional: None = None,
) -> Instance[T]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T]],
args: None = None,
*,
kwargs: Dict[str, Any],
factory: None = None,
optional: None = None,
) -> Instance[T]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T]],
args: None = None,
kwargs: None = None,
*,
factory: Callable[[], T],
optional: None = None,
) -> Instance[T]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T]],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: Optional[Callable[[], T]] = None,
optional: Literal[True],
) -> Instance[Optional[T]]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T]],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: Optional[Callable[[], T]] = None,
optional: Literal[False],
) -> Instance[T]: ...
# 2-Tuple[Any, ...]
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1]],
args: None = None,
kwargs: None = None,
*,
factory: None = None,
optional: None = None,
) -> Instance[Optional[T | T1]]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1]],
args: Tuple[Any, ...],
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: None = None,
optional: None = None,
) -> Instance[T | T1]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1]],
args: None = None,
*,
kwargs: Dict[str, Any],
factory: None = None,
optional: None = None,
) -> Instance[T | T1]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1]],
args: None = None,
kwargs: None = None,
*,
factory: Callable[[], T] | Callable[[], T1] | Callable[[], T | T1],
optional: None = None,
) -> Instance[T | T1]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1]],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: Optional[
Callable[[], T] | Callable[[], T1] | Callable[[], T | T1]
] = None,
optional: Literal[True],
) -> Instance[Optional[T | T1]]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1]],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: Optional[
Callable[[], T] | Callable[[], T1] | Callable[[], T | T1]
] = None,
optional: Literal[False],
) -> Instance[T | T1]: ...
# 3-Tuple[Any, ...]
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1], Type[T2]],
args: None = None,
kwargs: None = None,
*,
factory: None = None,
optional: None = None,
) -> Instance[Optional[T | T1 | T2]]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1], Type[T2]],
args: Tuple[Any, ...],
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: None = None,
optional: None = None,
) -> Instance[T | T1 | T2]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1], Type[T2]],
args: None = None,
*,
kwargs: Dict[str, Any],
factory: None = None,
optional: None = None,
) -> Instance[T | T1 | T2]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1], Type[T2]],
args: None = None,
kwargs: None = None,
*,
factory: Callable[[], T]
| Callable[[], T1]
| Callable[[], T2]
| Callable[[], T | T1]
| Callable[[], T | T2]
| Callable[[], T1 | T2]
| Callable[[], T | T1 | T2],
optional: None = None,
) -> Instance[T | T1 | T2]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1], Type[T2]],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: Optional[
Callable[[], T]
| Callable[[], T1]
| Callable[[], T2]
| Callable[[], T | T1]
| Callable[[], T | T2]
| Callable[[], T1 | T2]
| Callable[[], T | T1 | T2]
] = None,
optional: Literal[True],
) -> Instance[Optional[T | T1 | T2]]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1], Type[T2]],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: Optional[
Callable[[], T]
| Callable[[], T1]
| Callable[[], T2]
| Callable[[], T | T1]
| Callable[[], T | T2]
| Callable[[], T1 | T2]
| Callable[[], T | T1 | T2]
] = None,
optional: Literal[False],
) -> Instance[T | T1 | T2]: ...
class ForwardInstance(Member[T, T]):
# Single Type
@overload
def __new__(
cls,
kind: Callable[[], Type[T]],
args: None = None,
kwargs: None = None,
*,
factory: None = None,
optional: None = None,
) -> ForwardInstance[Optional[T]]: ...
@overload
def __new__(
cls,
kind: Callable[[], Type[T]],
args: Tuple[Any, ...],
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: None = None,
optional: None = None,
) -> ForwardInstance[T]: ...
@overload
def __new__(
cls,
kind: Callable[[], Type[T]],
args: None = None,
*,
kwargs: Dict[str, Any],
factory: None = None,
optional: None = None,
) -> ForwardInstance[T]: ...
@overload
def __new__(
cls,
kind: Callable[[], Type[T]],
args: None = None,
kwargs: None = None,
*,
factory: Callable[[], T],
optional: None = None,
) -> ForwardInstance[T]: ...
@overload
def __new__(
cls,
kind: Callable[[], Type[T]],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: Optional[Callable[[], T]] = None,
optional: Literal[True],
) -> ForwardInstance[Optional[T]]: ...
@overload
def __new__(
cls,
kind: Callable[[], Type[T]],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: Optional[Callable[[], T]] = None,
optional: Literal[False],
) -> ForwardInstance[T]: ...
# 1-Tuple[Any, ...]
@overload
def __new__(
cls,
kind: Callable[[], Tuple[Type[T]]],
args: None = None,
kwargs: None = None,
*,
factory: None = None,
optional: None = None,
) -> ForwardInstance[Optional[T]]: ...
@overload
def __new__(
cls,
kind: Callable[[], Tuple[Type[T]]],
args: Tuple[Any, ...],
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: None = None,
optional: None = None,
) -> ForwardInstance[T]: ...
@overload
def __new__(
cls,
kind: Callable[[], Tuple[Type[T]]],
args: None = None,
*,
kwargs: Dict[str, Any],
factory: None = None,
optional: None = None,
) -> ForwardInstance[T]: ...
@overload
def __new__(
cls,
kind: Callable[[], Tuple[Type[T]]],
args: None = None,
kwargs: None = None,
*,
factory: Callable[[], T],
optional: None = None,
) -> ForwardInstance[T]: ...
@overload
def __new__(
cls,
kind: Callable[[], Tuple[Type[T]]],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: Optional[Callable[[], T]] = None,
optional: Literal[True],
) -> ForwardInstance[Optional[T]]: ...
@overload
def __new__(
cls,
kind: Callable[[], Tuple[Type[T]]],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: Optional[Callable[[], T]] = None,
optional: Literal[False],
) -> ForwardInstance[T]: ...
# 2-Tuple[Any, ...]
@overload
def __new__(
cls,
kind: Callable[[], Tuple[Type[T], Type[T1]]],
args: None = None,
kwargs: None = None,
*,
factory: None = None,
optional: None = None,
) -> ForwardInstance[Optional[T | T1]]: ...
@overload
def __new__(
cls,
kind: Callable[[], Tuple[Type[T], Type[T1]]],
args: Tuple[Any, ...],
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: None = None,
optional: None = None,
) -> ForwardInstance[T | T1]: ...
@overload
def __new__(
cls,
kind: Callable[[], Tuple[Type[T], Type[T1]]],
args: None = None,
*,
kwargs: Dict[str, Any],
factory: None = None,
optional: None = None,
) -> ForwardInstance[T | T1]: ...
@overload
def __new__(
cls,
kind: Callable[[], Tuple[Type[T], Type[T1]]],
args: None = None,
kwargs: None = None,
*,
factory: Callable[[], T] | Callable[[], T1] | Callable[[], T | T1],
optional: None = None,
) -> ForwardInstance[T | T1]: ...
@overload
def __new__(
cls,
kind: Callable[[], Tuple[Type[T], Type[T1]]],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: Optional[
Callable[[], T] | Callable[[], T1] | Callable[[], T | T1]
] = None,
optional: Literal[True],
) -> ForwardInstance[Optional[T | T1]]: ...
@overload
def __new__(
cls,
kind: Callable[[], Tuple[Type[T], Type[T1]]],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: Optional[
Callable[[], T] | Callable[[], T1] | Callable[[], T | T1]
] = None,
optional: Literal[False],
) -> ForwardInstance[T | T1]: ...
# 3-Tuple[Any, ...]
@overload
def __new__(
cls,
kind: Callable[[], Tuple[Type[T], Type[T1], Type[T2]]],
args: None = None,
kwargs: None = None,
*,
factory: None = None,
optional: None = None,
) -> ForwardInstance[Optional[T | T1 | T2]]: ...
@overload
def __new__(
cls,
kind: Callable[[], Tuple[Type[T], Type[T1], Type[T2]]],
args: Tuple[Any, ...],
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: None = None,
optional: None = None,
) -> ForwardInstance[T | T1 | T2]: ...
@overload
def __new__(
cls,
kind: Callable[[], Tuple[Type[T], Type[T1], Type[T2]]],
args: None = None,
*,
kwargs: Dict[str, Any],
factory: None = None,
optional: None = None,
) -> ForwardInstance[T | T1 | T2]: ...
@overload
def __new__(
cls,
kind: Callable[[], Tuple[Type[T], Type[T1], Type[T2]]],
args: None = None,
kwargs: None = None,
*,
factory: Callable[[], T]
| Callable[[], T1]
| Callable[[], T2]
| Callable[[], T | T1]
| Callable[[], T | T2]
| Callable[[], T1 | T2]
| Callable[[], T | T1 | T2],
optional: None = None,
) -> ForwardInstance[T | T1 | T2]: ...
@overload
def __new__(
cls,
kind: Callable[[], Tuple[Type[T], Type[T1], Type[T2]]],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: Optional[
Callable[[], T]
| Callable[[], T1]
| Callable[[], T2]
| Callable[[], T | T1]
| Callable[[], T | T2]
| Callable[[], T1 | T2]
| Callable[[], T | T1 | T2]
] = None,
optional: Literal[True],
) -> ForwardInstance[Optional[T | T1 | T2]]: ...
@overload
def __new__(
cls,
kind: Callable[[], Tuple[Type[T], Type[T1], Type[T2]]],
args: Optional[Tuple[Any, ...]] = None,
kwargs: Optional[Dict[str, Any]] = None,
*,
factory: Optional[
Callable[[], T]
| Callable[[], T1]
| Callable[[], T2]
| Callable[[], T | T1]
| Callable[[], T | T2]
| Callable[[], T1 | T2]
| Callable[[], T | T1 | T2]
] = None,
optional: Literal[False],
) -> ForwardInstance[T | T1 | T2]: ...
atom-0.12.1/atom/list.py 0000664 0000000 0000000 00000005261 15067567316 0015044 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2013-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from .catom import DefaultValue, Member, Validate
from .instance import Instance
from .typing_utils import extract_types, is_optional
class List(Member):
"""A member which allows list values.
Assigning to a list creates a copy. The orginal list will remain
unmodified. This is similar to the semantics of the assignment
operator on the C++ STL container classes.
"""
__slots__ = "item"
def __init__(self, item=None, default=None):
"""Initialize a List.
Parameters
----------
item : Member, type, or tuple of types, optional
A member to use for validating the types of items allowed in
the list. This can also be a type object or a tuple of types,
in which case it will be wrapped with an non-optional Instance
member. If this is not given, no item validation is performed.
default : list, optional
The default list of values. A new copy of this list will be
created for each atom instance.
"""
if item is not None and not isinstance(item, Member):
opt, types = is_optional(extract_types(item))
item = Instance(types, optional=opt)
self.item = item
self.set_default_value_mode(DefaultValue.List, default)
self.set_validate_mode(Validate.List, item)
def set_name(self, name):
"""Set the name of the member.
This method ensures that the item member name is also updated.
"""
super(List, self).set_name(name)
if self.item is not None:
self.item.set_name(name + "|item")
def set_index(self, index):
"""Assign the index to this member.
This method ensures that the item member index is also updated.
"""
super(List, self).set_index(index)
if self.item is not None:
self.item.set_index(index)
def clone(self):
"""Create a clone of the list.
This will clone the internal list item if one is in use.
"""
clone = super(List, self).clone()
item = self.item
if item is not None:
clone.item = item_clone = item.clone()
mode, _ctxt = self.validate_mode
clone.set_validate_mode(mode, item_clone)
else:
clone.item = None
return clone
atom-0.12.1/atom/list.pyi 0000664 0000000 0000000 00000004435 15067567316 0015217 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2021-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from typing import Any, List as TList, Optional, Tuple, Type, TypeVar, overload
from .catom import Member
T = TypeVar("T")
T1 = TypeVar("T1")
T2 = TypeVar("T2")
class List(Member[TList[T], TList[T]]):
# No default
@overload
def __new__(
cls, kind: None = None, default: Optional[TList[Any]] = None
) -> List[Any]: ...
@overload
def __new__(cls, kind: Type[T], default: None = None) -> List[T]: ...
@overload
def __new__(cls, kind: Tuple[Type[T]], default: None = None) -> List[T]: ...
@overload
def __new__(
cls, kind: Tuple[Type[T], Type[T1]], default: None = None
) -> List[T | T1]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1], Type[T2]],
default: None = None,
) -> List[T | T1 | T2]: ...
@overload
def __new__(cls, kind: Member[T, Any], default: None = None) -> List[T]: ...
# With default
@overload
def __new__(cls, kind: Type[T], default: TList[T]) -> List[T]: ...
@overload
def __new__(cls, kind: Tuple[Type[T]], default: TList[T]) -> List[T]: ...
@overload
def __new__(
cls, kind: Tuple[Type[T], Type[T1]], default: TList[T | T1]
) -> List[T | T1]: ...
@overload
def __new__(
cls, kind: Tuple[Type[T], Type[T1]], default: TList[T] | TList[T1]
) -> List[T | T1]: ...
@overload
def __new__(
cls, kind: Tuple[Type[T], Type[T1], Type[T2]], default: TList[T | T1 | T2]
) -> List[T | T1 | T2]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1], Type[T2]],
default: TList[T | T1] | TList[T | T2] | TList[T1 | T2],
) -> List[T | T1 | T2]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1], Type[T2]],
default: TList[T] | TList[T1] | TList[T2],
) -> List[T | T1 | T2]: ...
@overload
def __new__(cls, kind: Member[T, Any], default: TList[T]) -> List[T]: ...
atom-0.12.1/atom/meta/ 0000775 0000000 0000000 00000000000 15067567316 0014441 5 ustar 00root root 0000000 0000000 atom-0.12.1/atom/meta/__init__.py 0000664 0000000 0000000 00000001325 15067567316 0016553 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2023-2024, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
"""Atom metaclass and tools used to create atom subclasses."""
from .atom_meta import AtomMeta, MissingMemberWarning, add_member, clone_if_needed
from .member_modifiers import set_default
from .observation import observe
__all__ = [
"AtomMeta",
"MissingMemberWarning",
"add_member",
"clone_if_needed",
"observe",
"set_default",
]
atom-0.12.1/atom/meta/annotation_utils.py 0000664 0000000 0000000 00000016356 15067567316 0020420 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2021-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
import collections.abc
import sys
from collections import defaultdict
from typing import Any, ClassVar, Final, Literal, MutableMapping, Type
from ..catom import Member, SetAttr
from ..dict import DefaultDict, Dict as ADict
from ..enum import Enum
from ..instance import Instance
from ..list import List as AList
from ..scalars import Bool, Bytes, Callable as ACallable, Float, Int, Str, Value
from ..set import Set as ASet
from ..subclass import Subclass
from ..tuple import FixedTuple, Tuple as ATuple
from ..typed import Typed
from ..typing_utils import extract_types, get_args, get_origin, is_optional
from .member_modifiers import set_default
_NO_DEFAULT = object()
_TYPE_TO_MEMBER = {
bool: Bool,
int: Int,
float: Float,
str: Str,
bytes: Bytes,
list: AList,
dict: ADict,
defaultdict: DefaultDict,
set: ASet,
tuple: ATuple,
collections.abc.Callable: ACallable,
}
def generate_member_from_type_or_generic(
type_generic: Any, default: Any, annotate_type_containers: int
) -> Member:
"""Generate a member from a type or generic alias."""
types: tuple[type, ...]
origin = get_origin(type_generic)
# For final we create the member corresponding to the inner type and set
# the access mode as ReadOnly
if origin is Final:
member = generate_member_from_type_or_generic(
get_args(type_generic)[0], default, annotate_type_containers
)
member.set_setattr_mode(SetAttr.ReadOnly, None)
return member
# Here we special case Literal to generate an Enum member.
elif origin is Literal:
types = ()
else:
types = extract_types(type_generic)
parameters = get_args(type_generic)
m_kwargs = {}
m_cls: Type[Member]
if any(
isinstance(t, type) and issubclass(t, Member) for t in types
) and not isinstance(default, Member):
raise ValueError(
"Member subclasses cannot be used as annotations without "
"specifying a default value for the attribute."
)
elif object in types or Any in types:
m_cls = Value
parameters = ()
# We are dealing with a Literal, so use an Enum member
elif not types:
m_cls = Enum
if default is not _NO_DEFAULT:
if default not in parameters:
raise ValueError(
f"Default value {default} does not appear in Literal: {parameters}"
)
# Make the default value the first in the enum arguments.
p = list(parameters)
p.pop(p.index(default))
parameters = (default, *p)
default = _NO_DEFAULT
# Int, Float, Str, Bytes, List, Dict, Set, Tuple, Bool, Callable
elif len(types) == 1 and types[0] in _TYPE_TO_MEMBER:
t = types[0]
m_cls = _TYPE_TO_MEMBER[t]
if annotate_type_containers and t in (
list,
dict,
collections.defaultdict,
set,
tuple,
):
if t is tuple:
if (...) in parameters:
parameters = (parameters[0],)
else:
m_cls = FixedTuple
parameters = tuple(
generate_member_from_type_or_generic(
t, _NO_DEFAULT, annotate_type_containers - 1
)
if t not in (Any, object)
else Value()
for t in parameters
)
else:
parameters = ()
# The value was annotated with Type[T] so we use a subclass
elif all(t is type for t in types):
m_cls = Subclass
assert len(parameters) == 1
parameters = extract_types(parameters[0])
else:
# Only a metaclass can implement __instancecheck__ so we check for an
# implementation differing from type.__instancecheck__ and use Instance
# if we find one and otherwise Typed.
opt, filtered_types = is_optional(types)
if (
len(filtered_types) == 1
and type(filtered_types[0]).__instancecheck__ is type.__instancecheck__
):
m_cls = Typed
else:
m_cls = Instance
parameters = (filtered_types,)
m_kwargs["optional"] = opt
if opt and default not in (_NO_DEFAULT, None):
raise ValueError(
"Members requiring Instance(optional=True) cannot have "
"a non-None default value."
)
elif not opt and default is not _NO_DEFAULT:
raise ValueError("Members requiring Instance cannot have a default value.")
# Instance does not have a default keyword so turn a None default into the
# equivalent no default.
default = _NO_DEFAULT
if default is not _NO_DEFAULT:
m_kwargs["default"] = default
return m_cls(*parameters, **m_kwargs)
def generate_members_from_cls_namespace(
cls_name: str, namespace: MutableMapping[str, Any], annotate_type_containers: int
) -> None:
"""Generate the member corresponding to a type annotation."""
# On 3.14 use annotationlib
# cf https://docs.python.org/3.14/library/annotationlib.html
if sys.version_info >= (3, 14):
import annotationlib
if "__annotations__" in namespace:
annotations = namespace["__annotations__"]
else:
annotate = annotationlib.get_annotate_from_class_namespace(namespace)
if annotate is None:
annotations = {}
else:
annotations = annotationlib.call_annotate_function(
annotate, format=annotationlib.Format.FORWARDREF
)
else:
annotations = namespace.get("__annotations__", {})
# XXX handle forward refs
for name, ann in annotations.items():
default = namespace.get(name, _NO_DEFAULT)
# We skip field for which a member was already provided or annotations
# corresponding to class variables.
if isinstance(default, (Member, set_default)):
# Allow string annotations for members
if isinstance(ann, str):
continue
types = extract_types(ann)
if len(types) != 1 or not issubclass(types[0], Member):
raise TypeError(
f"Field '{name}' of '{cls_name}' is assigned a Member-like value "
"but its annotation is not Member compatible"
)
continue
# We also skip fields annotated as class variables.
elif getattr(ann, "__origin__", None) is ClassVar:
continue
try:
namespace[name] = generate_member_from_type_or_generic(
ann, default, annotate_type_containers
)
except ValueError as e:
raise ValueError(
"Encountered an issue when generating a member for field "
f"'{name}' of '{cls_name}'."
) from e
atom-0.12.1/atom/meta/atom_meta.py 0000664 0000000 0000000 00000046107 15067567316 0016771 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2023-2024, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
"""Metaclass implementing atom members customization."""
import copyreg
import warnings
from types import FunctionType
from typing import (
Any,
Callable,
Dict,
FrozenSet,
List,
Mapping,
MutableMapping,
Optional,
Sequence,
Set,
Tuple,
TypeVar,
Union,
)
from ..catom import (
CAtom,
DefaultValue,
GetState,
Member,
PostGetAttr,
PostSetAttr,
PostValidate,
Validate,
)
from .annotation_utils import generate_members_from_cls_namespace
from .member_modifiers import set_default
from .observation import ExtendedObserver, ObserveHandler
OBSERVE_PREFIX = "_observe_"
DEFAULT_PREFIX = "_default_"
VALIDATE_PREFIX = "_validate_"
POST_GETATTR_PREFIX = "_post_getattr_"
POST_SETATTR_PREFIX = "_post_setattr_"
POST_VALIDATE_PREFIX = "_post_validate_"
GETSTATE_PREFIX = "_getstate_"
M = TypeVar("M", bound=Member)
def add_member(cls: "AtomMeta", name: str, member: Member) -> None:
"""Add or override a member after the class creation."""
existing = cls.__atom_members__.get(name)
if existing is not None:
member.set_index(existing.index)
member.copy_static_observers(existing)
else:
member.set_index(len(cls.__atom_members__))
member.set_name(name)
# The dict is mutable but we do not want to say it too loud
cls.__atom_members__[name] = member # type: ignore
cls.__atom_specific_members__ = frozenset(
set(cls.__atom_specific_members__) | {name}
)
setattr(cls, name, member)
def clone_if_needed(cls: "AtomMeta", member: M) -> M:
"""Clone a member if is not owned by a class.
This function is meant to be used in __init__subclass__ to safely
customize members static behaviors.
"""
members = dict(cls.__atom_members__)
specific_members = set(cls.__atom_specific_members__)
# This may lead to cloning some members that do not need to be but it
# should not be too costly
owned_members = {members[k] for k in cls.__atom_specific_members__}
m = _clone_if_needed(member, members, specific_members, owned_members)
setattr(cls, m.name, m)
cls.__atom_members__ = members
cls.__atom_specific_members__ = frozenset(specific_members)
return m
class MissingMemberWarning(UserWarning):
"""Signal an expected member is not present."""
pass
def _signal_missing_member(
owner: str, method: str, members: Mapping[str, Member], prefix: str
) -> None:
warnings.warn(
f"{prefix} method {method} on class '{owner}' does not match any member "
f"defined on the Atom object. Existing members are: {members}",
MissingMemberWarning,
stacklevel=3,
)
def _compute_mro(bases: Sequence[type]) -> List[type]:
"""Compute the MRO from a sequence of base classes using the C3 algorithm.
Adapted from https://www.python.org/download/releases/2.3/mro/.
"""
sequences = [b.mro() for b in bases]
computed_mro: List[type] = []
while True:
sequences = list(filter(None, sequences))
if not sequences:
return computed_mro
# Look for candidates in seq heads
candidate: Optional[type]
for s1 in sequences:
candidate = s1[0]
# Discard candidate if it appears in the tail of any of the sequence
if any(candidate in s[1:] for s in sequences):
candidate = None
else:
break
if candidate is None:
raise RuntimeError("Inconsistent hierarchy")
computed_mro.append(candidate)
# Remove the candidate from all sequences
for seq in sequences:
if seq[0] is candidate:
del seq[0]
def _clone_if_needed(
member: M,
members: Dict[str, Member],
specific_members: Set[str],
owned_members: Set[Member],
) -> M:
"""Clone a member if it cannot be safely modified."""
# The member may have been cloned due to slot conflicts but that
# does not make it specific. However, each member on which function
# is called is guaranteed to be specific.
specific_members.add(member.name)
if member not in owned_members:
member = member.clone()
members[member.name] = member
owned_members.add(member)
return member
class _AtomMetaHelper:
"""Helper class to create a Atom class."""
#: All members that can be accessed from the class we are helping create.
members: MutableMapping[str, Member]
#: The set of members which live on this class as opposed to a
#: base class. This enables the code which hooks up the various
#: static behaviors to only clone a member when necessary.
owned_members: Set[Member]
#: Names of the members whose behavior is specific to this class.
specific_members: Set[str]
#: Decorated observers: @observe
decorated: List[ObserveHandler]
#: The set of seen @observe decorators
seen_decorated: Set[ObserveHandler]
#: set_default() sentinel
set_defaults: List[set_default]
#: Static observer methods: _observe_*
observes: List[str]
#: Default value methods: _default_*
defaults: List[str]
#: Validator methods: _validate_*
validates: List[str]
#: Post getattr methods: _post_getattr_*
post_getattrs: List[str]
#: Post setattr methods: _post_setattr_*
post_setattrs: List[str]
#: Post validate methods: _post_validate_*
post_validates: List[str]
# Getstate methods: _getstate_*
getstates: List[str]
__slots__ = (
"bases",
"dct",
"decorated",
"defaults",
"getstates",
"members",
"name",
"observes",
"owned_members",
"post_getattrs",
"post_setattrs",
"post_validates",
"seen_decorated",
"set_defaults",
"specific_members",
"validates",
)
def __init__(self, name: str, bases: Tuple[type, ...], dct: Dict[str, Any]) -> None:
self.name = name
self.bases = bases
self.dct = dct
self.members = {}
self.owned_members = set()
self.specific_members = set()
self.observes = []
self.defaults = []
self.validates = []
self.decorated = []
self.set_defaults = []
self.post_getattrs = []
self.post_setattrs = []
self.post_validates = []
self.getstates = []
self.seen_decorated = set()
def scan_and_clear_namespace(self) -> None:
"""Collect information necessary to implement the various behaviors.
Some objects declared on the class only serve as sentinels, and they are
removed from the dict before creating the class.
"""
dct = self.dct
seen_sentinels = set() # The set of seen sentinels
for key, value in dct.items():
if isinstance(value, set_default):
if value in seen_sentinels:
value = value.clone()
value.name = key
self.set_defaults.append(value)
seen_sentinels.add(value)
continue
if isinstance(value, ObserveHandler):
if value in self.seen_decorated:
value = value.clone()
self.seen_decorated.add(value)
self.decorated.append(value)
value.funcname = key
value = value.func
dct[key] = value
# Coninue processing the unwrapped function
if isinstance(value, FunctionType):
if key.startswith(OBSERVE_PREFIX):
self.observes.append(key)
elif key.startswith(DEFAULT_PREFIX):
self.defaults.append(key)
elif key.startswith(VALIDATE_PREFIX):
self.validates.append(key)
elif key.startswith(POST_VALIDATE_PREFIX):
self.post_validates.append(key)
elif key.startswith(POST_GETATTR_PREFIX):
self.post_getattrs.append(key)
elif key.startswith(POST_SETATTR_PREFIX):
self.post_setattrs.append(key)
elif key.startswith(GETSTATE_PREFIX):
self.getstates.append(key)
# Remove the sentinels from the dict before creating the class.
# The sentinels for the @observe decorators are already removed.
for s in seen_sentinels:
assert s.name
del dct[s.name]
def assign_members_indexes(self) -> None:
"""Walk the MRO to assign a unique index to each member."""
# Compute the class MRO.
cls_mro = _compute_mro(self.bases)
# Walk the mro of the class, excluding itself, in reverse order
# collecting all of the members into a single dict. The reverse
# update preserves the mro of overridden members. We use only known
# specific members to also preserve the mro in presence of multiple
# inheritance.
members = self.members
for base in reversed(cls_mro[:-1]):
if base is not CAtom and issubclass(base, CAtom):
# Except if somebody abuses the system and create a non-Atom subclass
# of CAtom, this works
# Mypy does not narrow the type from the above test hence the ignores
members.update(
{
k: v
for k, v in base.__atom_members__.items() # type: ignore
if k in base.__atom_specific_members__ # type: ignore
}
)
# Resolve any conflicts with memory layout. Conflicts can occur
# with multiple inheritance where the indices of multiple base
# classes will overlap. When this happens, the members which
# conflict must be cloned in order to occupy a unique index.
conflicts = []
occupied = set()
for member in members.values():
if member.index in occupied:
conflicts.append(member)
else:
occupied.add(member.index)
# Track the first available index
i = 0
def get_first_free_index() -> int:
nonlocal i
while i in occupied:
i += 1
occupied.add(i)
return i
# Clone the conflicting members and give them a unique index.
# Do not blow away an overridden item on the current class.
owned_members = self.owned_members
cloned = {}
for member in conflicts:
clone = member.clone()
clone.set_index(get_first_free_index())
owned_members.add(clone)
members[clone.name] = clone
if clone.name not in self.dct:
cloned[clone.name] = clone
# Walk the dict a second time to collect the class members. This
# assigns the name and the index to the member. If a member is
# overriding an existing member, the memory index of the old
# member is reused and any static observers are copied over.
specific_members = self.specific_members
for key, value in self.dct.items():
if isinstance(value, Member):
if value in owned_members: # foo = bar = Baz()
value = value.clone()
self.dct[key] = value
value.set_name(key)
owned_members.add(value)
specific_members.add(value.name)
if key in members:
supermember = members[key]
members[key] = value
value.set_index(supermember.index)
value.copy_static_observers(supermember)
else:
value.set_index(get_first_free_index())
members[key] = value
# Ensure we have a contiguous array for members
assert occupied == set(range(len(members)))
# Add cloned members to the class dictionary
self.dct.update(cloned)
def apply_members_static_behaviors(self) -> None:
"""Add the special statically defined behaviors for the members.
If the target member is defined on a subclass, it is cloned
so that the behavior of the subclass is not modified.
"""
members = self.members
def clone_if_needed(m):
m = _clone_if_needed(m, members, self.specific_members, self.owned_members)
self.dct[m.name] = m
return m
# set_default() sentinels
for sd in self.set_defaults:
assert sd.name # At this point the name has been set
if sd.name not in members:
msg = "Invalid call to set_default(). '%s' is not a member "
msg += "on the '%s' class."
raise TypeError(msg % (sd.name, self.name))
member = clone_if_needed(members[sd.name])
member.set_default_value_mode(DefaultValue.Static, sd.value)
# _default_* methods
for prefix, method_names, mode_setter in [
(
DEFAULT_PREFIX,
self.defaults,
lambda m, name: m.set_default_value_mode(
DefaultValue.ObjectMethod, name
),
),
(
VALIDATE_PREFIX,
self.validates,
lambda m, name: m.set_validate_mode(Validate.ObjectMethod_OldNew, name),
),
(
POST_VALIDATE_PREFIX,
self.post_validates,
lambda m, name: m.set_post_validate_mode(
PostValidate.ObjectMethod_OldNew, name
),
),
(
POST_GETATTR_PREFIX,
self.post_getattrs,
lambda m, name: m.set_post_getattr_mode(
PostGetAttr.ObjectMethod_Value, name
),
),
(
POST_SETATTR_PREFIX,
self.post_setattrs,
lambda m, name: m.set_post_setattr_mode(
PostSetAttr.ObjectMethod_OldNew, name
),
),
(
GETSTATE_PREFIX,
self.getstates,
lambda m, name: m.set_getstate_mode(GetState.ObjectMethod_Name, name),
),
]:
n = len(prefix)
for mangled in method_names:
target = mangled[n:]
if target in members:
member = clone_if_needed(members[target])
mode_setter(member, mangled)
else:
_signal_missing_member(self.name, mangled, members, prefix)
# _observe_* methods
n = len(OBSERVE_PREFIX)
for mangled in self.observes:
target = mangled[n:]
if target in members:
member = clone_if_needed(members[target])
member.add_static_observer(mangled)
else:
_signal_missing_member(self.name, mangled, members, OBSERVE_PREFIX)
# @observe decorated methods
for handler in self.decorated:
assert handler.funcname # Set at this point
change_types = handler.change_types
for name, attr in handler.pairs:
if name in members:
member = clone_if_needed(members[name])
observer: Union[str, Callable[..., None]]
observer = handler.funcname
if attr is not None:
observer = ExtendedObserver(observer, attr)
member.add_static_observer(observer, change_types)
else:
_signal_missing_member(
self.name, name, members, "observe decorated"
)
def create_class(self, meta: type) -> type:
"""Create the class after adding class variables."""
# Put a reference to the members dict on the class. This is used
# by CAtom to query for the members and member count as needed.
self.dct["__atom_members__"] = self.members
# Keep a reference to the specific members dict on the class. Specific
# members are members which are defined or altered in the class. This
# is used to ensure proper MRO resolution for members.
self.dct["__atom_specific_members__"] = frozenset(
m for m in self.specific_members
)
# Create the class object.
# We do it once everything else has been setup so that if users wants
# to use __init__subclass__ they have access to fully initialized
# Atom type.
cls: type = type.__new__(meta, self.name, self.bases, self.dct)
# Generate slotnames cache
# (using a private function that mypy does not know about).
copyreg._slotnames(cls) # type: ignore
return cls
class AtomMeta(type):
"""The metaclass for classes derived from Atom.
This metaclass computes the memory layout of the members in a given
class so that the CAtom class can allocate exactly enough space for
the object data slots when it instantiates an object.
All classes deriving from Atom will be automatically slotted, which
will prevent the creation of an instance dictionary and also the
ability of an Atom to be weakly referenceable. If that behavior is
required, then a subclasss should declare the appropriate slots.
"""
__atom_members__: Mapping[str, Member]
__atom_specific_members__: FrozenSet[str]
def __new__(
meta,
name: str,
bases: Tuple[type, ...],
dct: Dict[str, Any],
enable_weakrefs: bool = False,
use_annotations: bool = True,
type_containers: int = 1,
):
# Ensure there is no weird mro calculation and that we can use our
# re-implementation of C3
assert meta.mro is type.mro, "Custom MRO calculation are not supported"
# Unless the developer requests slots, they are automatically
# turned off. This prevents the creation of instance dicts and
# other space consuming features unless explicitly requested.
if "__slots__" not in dct:
dct["__slots__"] = ()
if enable_weakrefs:
dct["__slots__"] += ("__weakref__",)
if use_annotations:
generate_members_from_cls_namespace(name, dct, type_containers)
# Create the helper used to analyze the namespace and customize members
helper = _AtomMetaHelper(name, bases, dct)
# Analyze and clean the namespace
helper.scan_and_clear_namespace()
# Assign each member a unique ID
helper.assign_members_indexes()
# Customize the members based on the specified static modifiers
helper.apply_members_static_behaviors()
return helper.create_class(meta)
atom-0.12.1/atom/meta/member_modifiers.py 0000664 0000000 0000000 00000002204 15067567316 0020321 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2023-2024, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
"""Custom marker objects used to modify the default settings of a member."""
from typing import Any, Optional
class set_default(object):
"""An object used to set the default value of a base class member."""
__slots__ = ("name", "value")
#: Name of the member for which a new default value should be set. Used by
#: the metaclass.
name: Optional[str]
#: New default value to be set.
value: Any
def __init__(self, value: Any) -> None:
self.value = value
self.name = None # storage for the metaclass
def clone(self) -> "set_default":
"""Create a clone of the sentinel."""
return type(self)(self.value)
# XXX add more sentinels here to allow customizing members without using the
# members themselves:
# - tag
#
atom-0.12.1/atom/meta/observation.py 0000664 0000000 0000000 00000013276 15067567316 0017357 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2023-2024, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
"""Tools to declare static observers in Atom subclasses"""
from types import FunctionType
from typing import (
TYPE_CHECKING,
Any,
Callable,
List,
Mapping,
Optional,
Tuple,
TypeVar,
Union,
)
from ..catom import ChangeType
from ..typing_utils import ChangeDict
if TYPE_CHECKING:
from ..atom import Atom
def observe(*names: str, change_types: ChangeType = ChangeType.ANY) -> "ObserveHandler":
"""A decorator which can be used to observe members on a class.
Parameters
----------
*names
The str names of the attributes to observe on the object.
These must be of the form 'foo' or 'foo.bar'.
change_types
The flag specifying the type of changes to observe.
"""
# backwards compatibility for a single tuple or list argument
if len(names) == 1 and isinstance(names[0], (tuple, list)):
names = names[0]
pairs: List[Tuple[str, Optional[str]]] = []
for name in names:
if not isinstance(name, str):
msg = "observe attribute name must be a string, got '%s' instead"
raise TypeError(msg % type(name).__name__)
ndots = name.count(".")
if ndots > 1:
msg = "cannot observe '%s', only a single extension is allowed"
raise TypeError(msg % name)
if ndots == 1:
name, attr = name.split(".")
pairs.append((name, attr))
else:
pairs.append((name, None))
return ObserveHandler(pairs, change_types)
T = TypeVar("T", bound="Atom")
class ObserveHandler(object):
"""An object used to temporarily store observe decorator state."""
__slots__ = ("change_types", "func", "funcname", "pairs")
#: List of 2-tuples which stores the pair information for the observers.
pairs: List[Tuple[str, Optional[str]]]
#: Callable to be used as observer callback.
func: Optional[Callable[[Mapping[str, Any]], None]]
#: Name of the callable. Used by the metaclass.
funcname: Optional[str]
#: Types of changes to listen to.
change_types: ChangeType
def __init__(
self,
pairs: List[Tuple[str, Optional[str]]],
change_types: ChangeType = ChangeType.ANY,
) -> None:
"""Initialize an ObserveHandler.
Parameters
----------
pairs : list
The list of 2-tuples which stores the pair information for the observers.
"""
self.pairs = pairs
self.change_types = change_types
self.func = None # set by the __call__ method
self.funcname = None
def __call__(
self,
func: Union[
Callable[[ChangeDict], None],
Callable[[T, ChangeDict], None],
# AtomMeta will replace ObserveHandler in the body of an atom
# class allowing to access it for example in a subclass. We lie here by
# giving ObserverHandler.__call__ a signature compatible with an
# observer to mimic this behavior.
ChangeDict,
],
) -> "ObserveHandler":
"""Called to decorate the function.
Parameters
----------
func
Should be either a callable taking as single argument the change
dictionary or a method declared on an Atom object.
"""
assert isinstance(func, FunctionType), "func must be a function"
self.func = func
return self
def clone(self) -> "ObserveHandler":
"""Create a clone of the sentinel."""
clone = type(self)(self.pairs, self.change_types)
clone.func = self.func
return clone
class ExtendedObserver(object):
"""A callable object used to implement extended observers."""
__slots__ = ("attr", "funcname")
#: Name of the function on the owner object which should be used as the observer.
funcname: str
#: Attribute name on the target object which should be observed.
attr: str
def __init__(self, funcname: str, attr: str) -> None:
"""Initialize an ExtendedObserver.
Parameters
----------
funcname : str
The function name on the owner object which should be
used as the observer.
attr : str
The attribute name on the target object which should be
observed.
"""
self.funcname = funcname
self.attr = attr
def __call__(self, change: ChangeDict) -> None:
"""Handle a change of the target object.
This handler will remove the old observer and attach a new
observer to the target attribute. If the target object is not
an Atom object, an exception will be raised.
"""
from ..atom import Atom
old = None
new = None
ctype = change["type"]
if ctype == "create":
new = change["value"]
elif ctype == "update":
old = change["oldvalue"]
new = change["value"]
elif ctype == "delete":
old = change["value"]
attr = self.attr
owner = change["object"]
handler = getattr(owner, self.funcname)
if isinstance(old, Atom):
old.unobserve(attr, handler)
if isinstance(new, Atom):
new.observe(attr, handler)
elif new is not None:
msg = "cannot attach observer '%s' to non-Atom %s"
raise TypeError(msg % (attr, new))
atom-0.12.1/atom/property.py 0000664 0000000 0000000 00000011144 15067567316 0015752 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2013-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from .catom import DelAttr, GetAttr, GetState, Member, SetAttr, reset_property
class Property(Member):
"""A Member which behaves similar to a Python property."""
__slots__ = ()
def __init__(self, fget=None, fset=None, fdel=None, cached=False):
"""Initialize a Property member.
Parameters
----------
fget : callable or None, optional
The callable invoked to get the property value. It must
accept a single argument which is the owner object. If not
provided, the property cannot be read. The default is None.
fset : callable or None, optional
The callable invoked to set the property value. It must
accept two arguments: the owner object and property value.
If not provided, the property cannot be set. The default
is None.
fdel : callable or None, optional
The callable invoked to delete the property value. It must
accept a single argument which is the owner object. If not
provided, the property cannot be deleted. The default is
None.
cached : bool, optional
Whether or not the property caches the computed value. A
cached property will only evaluate 'fget' once until the
'reset' method of the property is invoked. The default is
False.
"""
gm = GetAttr.CachedProperty if cached else GetAttr.Property
self.set_getattr_mode(gm, fget)
if cached and fset is not None:
raise ValueError(
"Cached property are read-only, but a setter was specified."
)
self.set_setattr_mode(SetAttr.Property, fset)
self.set_delattr_mode(DelAttr.Property, fdel)
self.set_getstate_mode(GetState.Property, None)
@property
def fget(self):
"""Get the getter function for the property.
This will not find a specially named _get_* function.
"""
return self.getattr_mode[1]
@property
def fset(self):
"""Get the setter function for the property.
This will not find a specially named _set_* function.
"""
return self.setattr_mode[1]
@property
def fdel(self):
"""Get the deleter function for the property.
This will not find a specially named _del_* function.
"""
return self.delattr_mode[1]
@property
def cached(self):
"""Test whether or not this is a cached property."""
return self.getattr_mode[0] == GetAttr.CachedProperty
def getter(self, func):
"""Use the given function as the property getter.
This method is intended to be used as a decorator. The original
function will still be callable.
"""
mode, _ignored = self.getattr_mode
self.set_getattr_mode(mode, func)
return func
def setter(self, func):
"""Use the given function as the property setter.
This method is intended to be used as a decorator. The original
function will still be callable.
"""
if self.cached:
raise ValueError(
"Cached property are read-only, but a setter was specified."
)
self.set_setattr_mode(SetAttr.Property, func)
return func
def deleter(self, func):
"""Use the given function as the property deleter.
This method is intended to be used as a decorator. The original
function will still be callable.
"""
self.set_delattr_mode(DelAttr.Property, func)
return func
def reset(self, owner):
"""Reset the value of the property.
The old property value will be cleared and the notifiers will
be run if the new value is different from the old value. If
the property is not cached, notifiers will be unconditionally
run using None as the old value.
"""
reset_property(self, owner)
def cached_property(fget):
"""A decorator which converts a function into a cached Property.
Parameters
----------
fget : callable
The callable invoked to get the property value. It must accept
a single argument which is the owner object.
"""
return Property(fget, cached=True)
atom-0.12.1/atom/property.pyi 0000664 0000000 0000000 00000004052 15067567316 0016123 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2021-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from typing import Callable, NoReturn, Optional, TypeVar, overload
from .atom import Atom
from .catom import Member
A = TypeVar("A", bound=Atom)
T = TypeVar("T")
S = TypeVar("S")
class Property(Member[T, S]):
@overload
def __new__(
cls,
fget: None = None,
fset: None = None,
fdel: Optional[Callable[[A], None]] = None,
cached: bool = False,
) -> Property[NoReturn, NoReturn]: ...
@overload
def __new__(
cls,
fget: None,
fset: Callable[[A, S], None],
fdel: Optional[Callable[[A], None]] = None,
cached: bool = False,
) -> Property[NoReturn, S]: ...
@overload
def __new__(
cls,
fget: Callable[[A], T],
fset: None = None,
fdel: Optional[Callable[[A], None]] = None,
cached: bool = False,
) -> Property[T, NoReturn]: ...
@overload
def __new__(
cls,
fget: Callable[[A], T],
fset: Callable[[A, S], None],
fdel: Optional[Callable[[A], None]] = None,
cached: bool = False,
) -> Property[T, S]: ...
@property
def fget(self) -> Optional[Callable[[Atom], T]]: ...
@property
def fset(self) -> Optional[Callable[[Atom, S], None]]: ...
@property
def fdel(self) -> Optional[Callable[[Atom], None]]: ...
@property
def cached(self) -> bool: ...
def getter(self, func: Callable[[A], T]) -> Callable[[A], T]: ...
def setter(self, func: Callable[[A, S], None]) -> Callable[[A, S], None]: ...
def deleter(self, func: Callable[[A], None]) -> Callable[[A], None]: ...
def reset(self, owner: Atom) -> None: ...
def cached_property(fget: Callable[[A], T]) -> Property[T, NoReturn]: ...
atom-0.12.1/atom/py.typed 0000664 0000000 0000000 00000000000 15067567316 0015200 0 ustar 00root root 0000000 0000000 atom-0.12.1/atom/scalars.py 0000664 0000000 0000000 00000014702 15067567316 0015521 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2013-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from .catom import DefaultValue, DelAttr, GetState, Member, SetAttr, Validate
from .typing_utils import extract_types
class Value(Member):
"""A member class which supports value initialization.
A plain `Value` provides support for default values and factories,
but does not perform any type checking or validation. It serves as
a useful base class for scalar members and can be used for cases
where type checking is not needed (like private attributes).
"""
__slots__ = ()
def __init__(self, default=None, *, factory=None):
"""Initialize a Value.
Parameters
----------
default : object, optional
The default value for the member. If this is provided, it
should be an immutable value. The value will will not be
copied between owner instances.
factory : callable, optional
A callable object which is called with zero arguments and
returns a default value for the member. This will override
any value given by `default`.
"""
if factory is not None:
self.set_default_value_mode(DefaultValue.CallObject, factory)
else:
self.set_default_value_mode(DefaultValue.Static, default)
class ReadOnly(Value):
"""A value which can be assigned once and is then read-only."""
__slots__ = ()
def __init__(self, kind=None, *, default=None, factory=None):
super(ReadOnly, self).__init__(default, factory=factory)
self.set_setattr_mode(SetAttr.ReadOnly, None)
self.set_delattr_mode(DelAttr.ReadOnly, None)
self.set_getstate_mode(GetState.IncludeNonDefault, None)
if kind:
self.set_validate_mode(Validate.Instance, extract_types(kind))
class Constant(Value):
"""A value which cannot be changed from its default."""
__slots__ = ()
def __init__(self, default=None, *, factory=None, kind=None):
super(Constant, self).__init__(default, factory=factory)
self.set_setattr_mode(SetAttr.Constant, None)
self.set_delattr_mode(DelAttr.Constant, None)
self.set_getstate_mode(GetState.Exclude, None)
if kind:
self.set_validate_mode(Validate.Instance, extract_types(kind))
class Callable(Value):
"""A value which is callable."""
__slots__ = ()
def __init__(self, default=None, *, factory=None):
super(Callable, self).__init__(default, factory=factory)
self.set_validate_mode(Validate.Callable, None)
class Bool(Value):
"""A value of type `bool`."""
__slots__ = ()
def __init__(self, default=False, *, factory=None):
super(Bool, self).__init__(default, factory=factory)
self.set_validate_mode(Validate.Bool, None)
class Int(Value):
"""A value of type `int`.
By default, ints are strictly typed. Pass strict=False to the
constructor to enable int casting for longs and floats.
"""
__slots__ = ()
def __init__(self, default=0, *, factory=None, strict=True):
super(Int, self).__init__(default, factory=factory)
if strict:
self.set_validate_mode(Validate.Int, None)
else:
self.set_validate_mode(Validate.IntPromote, None)
class FloatRange(Value):
"""A float value clipped to a range.
By default, ints and longs will be promoted to floats. Pass
strict=True to the constructor to enable strict float checking.
"""
__slots__ = ()
def __init__(self, low=None, high=None, value=None, *, strict=False):
if low is not None and high is not None and low > high:
low, high = high, low
default = 0.0
if value is not None:
default = value
elif low is not None:
default = low
elif high is not None:
default = high
super(FloatRange, self).__init__(default)
if strict:
self.set_validate_mode(Validate.FloatRange, (low, high))
else:
if low is not None:
low = float(low)
if high is not None:
high = float(high)
self.set_validate_mode(Validate.FloatRangePromote, (low, high))
class Range(Value):
"""An integer value clipped to a range."""
__slots__ = ()
def __init__(self, low=None, high=None, value=None):
if low is not None and high is not None and low > high:
low, high = high, low
default = 0
if value is not None:
default = value
elif low is not None:
default = low
elif high is not None:
default = high
super(Range, self).__init__(default)
self.set_validate_mode(Validate.Range, (low, high))
class Float(Value):
"""A value of type `float`.
By default, ints and longs will be promoted to floats. Pass
strict=True to the constructor to enable strict float checking.
"""
__slots__ = ()
def __init__(self, default=0.0, *, factory=None, strict=False):
super(Float, self).__init__(default, factory=factory)
if strict:
self.set_validate_mode(Validate.Float, None)
else:
self.set_validate_mode(Validate.FloatPromote, None)
class Bytes(Value):
"""A value of type `bytes`.
By default, strings will NOT be promoted to bytes. Pass strict=False to the
constructor to enable loose byte checking.
"""
__slots__ = ()
def __init__(self, default=b"", *, factory=None, strict=True):
super(Bytes, self).__init__(default, factory=factory)
if strict:
self.set_validate_mode(Validate.Bytes, None)
else:
self.set_validate_mode(Validate.BytesPromote, None)
class Str(Value):
"""A value of type `str`.
By default, bytes will NOT be promoted to strings. Pass strict=False to the
constructor to enable loose string checking.
"""
def __init__(self, default="", *, factory=None, strict=True):
super(Str, self).__init__(default, factory=factory)
if strict:
self.set_validate_mode(Validate.Str, None)
else:
self.set_validate_mode(Validate.StrPromote, None)
atom-0.12.1/atom/scalars.pyi 0000664 0000000 0000000 00000015113 15067567316 0015667 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2021-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from typing import (
Any,
Callable as TCallable,
Literal,
NoReturn,
Optional,
Tuple,
Type,
TypeVar,
Union,
overload,
)
from .catom import Member
T = TypeVar("T")
T1 = TypeVar("T1")
T2 = TypeVar("T2")
S = TypeVar("S")
class Value(Member[T, T]):
def __new__(
cls, default: Any = None, *, factory: Optional[TCallable[[], Any]] = None
) -> Value[Any]: ...
class ReadOnly(Member[T, T]):
@overload
def __new__(
cls, kind: None = None, *, default: None = None, factory: None = None
) -> ReadOnly[Any]: ...
@overload
def __new__(
cls, kind: None = None, *, default: T, factory: None = None
) -> ReadOnly[T]: ...
@overload
def __new__(
cls, kind: None = None, *, default: None = None, factory: TCallable[[], T]
) -> ReadOnly[T]: ...
@overload
def __new__(
cls,
kind: Type[T],
*,
default: Optional[T] = None,
factory: Optional[TCallable[[], T]] = None,
) -> ReadOnly[T]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T]],
*,
default: Optional[T] = None,
factory: Optional[TCallable[[], T]] = None,
) -> ReadOnly[T]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1]],
*,
default: Optional[T | T1] = None,
factory: Optional[TCallable[[], T | T1]] = None,
) -> ReadOnly[T | T1]: ...
@overload
def __new__(
cls,
kind: Tuple[Type[T], Type[T1], Type[T2]],
*,
default: Optional[T | T1 | T2] = None,
factory: Optional[TCallable[[], T | T1 | T2]] = None,
) -> ReadOnly[T | T1 | T2]: ...
class Constant(Member[T, NoReturn]): # FIXME over-write del ?
@overload
def __new__(
cls, default: None = None, *, factory: None = None, kind: None = None
) -> Constant[Any]: ...
@overload
def __new__(
cls, default: None = None, *, factory: TCallable[[], T], kind: None = None
) -> Constant[T]: ...
@overload
def __new__(
cls, default: T, *, factory: None = None, kind: None = None
) -> Constant[T]: ...
@overload
def __new__(
cls,
default: Optional[T] = None,
*,
factory: Optional[TCallable[[], T]] = None,
kind: Type[T],
) -> Constant[T]: ...
@overload
def __new__(
cls,
default: Optional[T] = None,
*,
factory: Optional[TCallable[[], T]] = None,
kind: Tuple[Type[T]],
) -> Constant[T]: ...
@overload
def __new__(
cls,
default: Optional[T | T1] = None,
*,
factory: Optional[TCallable[[], T | T1]] = None,
kind: Tuple[Type[T], Type[T1]],
) -> Constant[T | T1]: ...
@overload
def __new__(
cls,
default: Optional[T | T1 | T2] = None,
*,
factory: Optional[TCallable[[], T | T1 | T2]] = None,
kind: Tuple[Type[T], Type[T1], Type[T2]],
) -> Constant[T | T1 | T2]: ...
C = TypeVar("C", bound=TCallable[..., Any])
class Callable(Member[T, T]):
@overload
def __new__(
cls, default: None = None, *, factory: TCallable[[], C]
) -> Callable[C]: ...
@overload
def __new__(cls, default: C, *, factory: None = None) -> Callable[C]: ...
@overload
def __new__(
cls, default: None = None, *, factory: None = None
) -> Callable[TCallable[..., Any]]: ...
class Bool(Member[bool, T]):
def __new__(
cls, default: bool = False, *, factory: Optional[TCallable[[], bool]] = None
) -> Bool[bool]: ...
class Int(Member[int, T]):
@overload
def __new__(
cls,
default: int = 0,
*,
factory: Optional[TCallable[[], int]] = None,
strict: Literal[True] = True,
) -> Int[int]: ...
@overload
def __new__(
cls,
default: Union[int, float] = 0,
*,
factory: Optional[TCallable[[], Union[int, float]]] = None,
strict: Literal[False],
) -> Int[Union[int, float]]: ...
# NOTE this cannot be properly statically typed checked since Mypy will always accept
# an int where a float is expected
class FloatRange(Member[float, T]):
@overload
def __new__(
cls,
low: Optional[float] = None,
high: Optional[float] = None,
value: Optional[float] = None,
*,
strict: Literal[False] = False,
) -> FloatRange[int | float]: ...
@overload
def __new__(
cls,
low: Optional[float] = None,
high: Optional[float] = None,
value: Optional[float] = None,
*,
strict: Literal[True],
) -> FloatRange[float]: ...
class Range(Member[int, T]):
def __new__(
cls,
low: Optional[int] = None,
high: Optional[int] = None,
value: Optional[int] = None,
) -> Range[int]: ...
class Float(Member[float, T]):
@overload
def __new__(
cls,
default: float = 0.0,
*,
factory: Optional[TCallable[[], Union[int, float]]] = None,
strict: Literal[False] = False,
) -> Float[Union[int, float]]: ...
@overload
def __new__(
cls,
default: float = 0.0,
*,
factory: Optional[TCallable[[], float]] = None,
strict: Literal[True],
) -> Float[float]: ... # FIXME we cannot encode that an int will be rejected
class Bytes(Member[bytes, T]):
@overload
def __new__(
cls,
default: bytes = b"",
*,
factory: Optional[TCallable[[], bytes]] = None,
strict: Literal[True] = True,
) -> Bytes[bytes]: ...
@overload
def __new__(
cls,
default: Union[str, bytes] = b"",
*,
factory: Optional[TCallable[[], Union[str, bytes]]] = None,
strict: Literal[False],
) -> Bytes[Union[bytes, str]]: ...
class Str(Member[str, T]):
@overload
def __new__(
cls,
default: str = "",
*,
factory: Optional[TCallable[[], str]] = None,
strict: Literal[True] = True,
) -> Str[str]: ...
@overload
def __new__(
cls,
default: Union[str, bytes] = "",
*,
factory: Optional[TCallable[[], Union[str, bytes]]] = None,
strict: Literal[False],
) -> Str[Union[str, bytes]]: ...
atom-0.12.1/atom/set.py 0000664 0000000 0000000 00000005564 15067567316 0014672 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2019-2024, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from .catom import DefaultValue, Member, Validate
from .instance import Instance
from .typing_utils import extract_types, is_optional
class Set(Member):
"""A member which allows set values.
Assigning to a set creates a copy. The original set will remain
unmodified. This is similar to the semantics of the assignment
operator on the C++ STL container classes.
"""
__slots__ = "item"
def __init__(self, item=None, default=None):
"""Initialize a Set.
Parameters
----------
item : Member, type, or tuple of types, optional
A member to use for validating the types of items allowed in
the set. This can also be a type object or a tuple of types,
in which case it will be wrapped with an Instance member. If
this is not given, no item validation is performed.
default : list, optional
The default list of values. A new copy of this list will be
created for each atom instance.
"""
self.set_default_value_mode(DefaultValue.Set, default)
if item is not None and not isinstance(item, Member):
opt, types = is_optional(extract_types(item))
item = Instance(types, optional=opt)
self.item = item
self.set_validate_mode(Validate.Set, item)
def set_name(self, name):
"""Assign the name to this member.
This method is called by the Atom metaclass when a class is
created. This makes sure the name of the internal members are
also updated.
"""
super(Set, self).set_name(name)
item = self.item
if item is not None:
item.set_name(name + "|item")
def set_index(self, index):
"""Assign the index to this member.
This method is called by the Atom metaclass when a class is
created. This makes sure the index of the internal members are
also updated.
"""
super(Set, self).set_index(index)
item = self.item
if item is not None:
item.set_index(index)
def clone(self):
"""Create a clone of the member.
This will clone the internal set item members if they exist.
"""
clone = super(Set, self).clone()
item = self.item
if item is not None:
clone.item = item_clone = item.clone()
mode, _ctxt = self.validate_mode
clone.set_validate_mode(mode, item_clone)
else:
clone.item = None
return clone
atom-0.12.1/atom/set.pyi 0000664 0000000 0000000 00000004501 15067567316 0015031 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2021-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from typing import Any, Optional, Set as TSet, Tuple, Type, TypeVar, overload
from .catom import Member
T = TypeVar("T")
T1 = TypeVar("T1")
T2 = TypeVar("T2")
class Set(Member[TSet[T], TSet[T]]):
@overload
def __new__(
cls, item: None = None, default: Optional[TSet[Any]] = None
) -> Set[Any]: ...
@overload
def __new__(cls, item: Type[T], default: None = None) -> Set[T]: ...
@overload
def __new__(cls, item: Tuple[Type[T]], default: None = None) -> Set[T]: ...
@overload
def __new__(
cls, item: Tuple[Type[T], Type[T1]], default: None = None
) -> Set[T | T1]: ...
@overload
def __new__(
cls,
item: Tuple[Type[T], Type[T1], Type[T2]],
default: None = None,
) -> Set[T | T1 | T2]: ...
@overload
def __new__(cls, item: Member[T, Any], default: None = None) -> Set[T]: ...
# With default
# The splitting is necessary otherwise Mypy type inference fails
@overload
def __new__(cls, item: Type[T], default: TSet[T]) -> Set[T]: ...
@overload
def __new__(cls, item: Tuple[Type[T]], default: TSet[T]) -> Set[T]: ...
@overload
def __new__(
cls, item: Tuple[Type[T], Type[T1]], default: TSet[T | T1]
) -> Set[T | T1]: ...
@overload
def __new__(
cls, item: Tuple[Type[T], Type[T1]], default: TSet[T] | TSet[T1]
) -> Set[T | T1]: ...
@overload
def __new__(
cls,
item: Tuple[Type[T], Type[T1], Type[T2]],
default: TSet[T | T1 | T2],
) -> Set[T | T1 | T2]: ...
@overload
def __new__(
cls,
item: Tuple[Type[T], Type[T1], Type[T2]],
default: TSet[T | T1] | TSet[T | T2] | TSet[T1 | T2],
) -> Set[T | T1 | T2]: ...
@overload
def __new__(
cls,
item: Tuple[Type[T], Type[T1], Type[T2]],
default: TSet[T] | TSet[T1] | TSet[T2],
) -> Set[T | T1 | T2]: ...
@overload
def __new__(cls, item: Member[T, Any], default: TSet[T]) -> Set[T]: ...
atom-0.12.1/atom/signal.py 0000664 0000000 0000000 00000001340 15067567316 0015340 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2013-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from .catom import DelAttr, GetAttr, Member, SetAttr
class Signal(Member):
"""A member which acts similar to a Qt signal."""
__slots__ = ()
def __init__(self):
"""Initialize a Signal."""
self.set_getattr_mode(GetAttr.Signal, None)
self.set_setattr_mode(SetAttr.Signal, None)
self.set_delattr_mode(DelAttr.Signal, None)
atom-0.12.1/atom/signal.pyi 0000664 0000000 0000000 00000000757 15067567316 0015524 0 ustar 00root root 0000000 0000000 # --------------------------------------------------------------------------------------
# Copyright (c) 2021-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# --------------------------------------------------------------------------------------
from typing import NoReturn
from .catom import Member, SignalConnector
class Signal(Member[SignalConnector, NoReturn]): ...
atom-0.12.1/atom/src/ 0000775 0000000 0000000 00000000000 15067567316 0014302 5 ustar 00root root 0000000 0000000 atom-0.12.1/atom/src/atomdict.cpp 0000664 0000000 0000000 00000030345 15067567316 0016617 0 ustar 00root root 0000000 0000000 /*-----------------------------------------------------------------------------
| Copyright (c) 2014-2024,, Nucleic
|
| Distributed under the terms of the BSD 3-Clause License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#include
#include
#include
#include "atomdict.h"
#include "packagenaming.h"
namespace atom
{
namespace
{
inline bool should_validate( AtomDict* dict )
{
return dict->pointer->data() && ( pyobject_cast( dict->m_key_validator ) != Py_None ||
pyobject_cast( dict->m_value_validator ) != Py_None );
}
PyObject* validate_key( AtomDict* dict, PyObject* key )
{
CAtom* atom = dict->pointer->data();
Member* key_val = dict->m_key_validator;
cppy::ptr key_item( cppy::incref( key ) );
if( key_val && atom )
{
key_item = key_val->full_validate( atom, Py_None, key_item.get() );
if( !key_item )
return 0;
}
return key_item.release();
}
PyObject* validate_value( AtomDict* dict, PyObject* value )
{
CAtom* atom = dict->pointer->data();
Member* value_val = dict->m_value_validator;
cppy::ptr value_item( cppy::incref( value ) );
if( value_val && atom )
{
value_item = value_val->full_validate( atom, Py_None, value_item.get() );
if( !value_item )
return 0;
}
return value_item.release();
}
int merge_items( PyObject* dict, PyObject* item, PyObject* kwargs )
{
int ok = 0;
if( item )
{
if( PyObject_HasAttrString( item, "keys" ) )
{
ok = PyDict_Merge( dict, item, 1 );
}
else
{
ok = PyDict_MergeFromSeq2( dict, item, 1 );
}
}
if( ok == 0 && kwargs )
{
ok = PyDict_Merge( dict, kwargs, 1 );
}
return ok;
}
PyObject* AtomDict_new( PyTypeObject* type, PyObject* args, PyObject* kwargs )
{
cppy::ptr self( PyDict_Type.tp_new( type, args, kwargs ) );
if( !self )
{
return 0; // LCOV_EXCL_LINE (failed instance creation)
}
atomdict_cast( self.get() )->pointer = new CAtomPointer();
return self.release();
}
int AtomDict_clear( AtomDict* self )
{
Py_CLEAR( self->m_key_validator );
Py_CLEAR( self->m_value_validator );
return PyDict_Type.tp_clear( pyobject_cast( self ) );
}
int AtomDict_traverse( AtomDict* self, visitproc visit, void* arg )
{
Py_VISIT( self->m_key_validator );
Py_VISIT( self->m_value_validator );
#if PY_VERSION_HEX >= 0x03090000
// This was not needed before Python 3.9 (Python issue 35810 and 40217)
Py_VISIT(Py_TYPE(self));
#endif
// PyDict_type is not heap allocated so it does visit the type
return PyDict_Type.tp_traverse( pyobject_cast( self ), visit, arg );
}
void AtomDict_dealloc( AtomDict* self )
{
PyObject_GC_UnTrack( self );
cppy::clear( &self->m_key_validator );
cppy::clear( &self->m_value_validator );
delete atomdict_cast( self )->pointer;
atomdict_cast( self )->pointer = 0;
PyDict_Type.tp_dealloc( pyobject_cast( self ) );
}
int AtomDict_ass_subscript( AtomDict* self, PyObject* key, PyObject* value )
{
cppy::ptr key_ptr( cppy::incref( key ) );
cppy::ptr value_ptr( cppy::xincref( value ) );
if( value && should_validate( self ) )
{
key_ptr = validate_key( self, key_ptr.get() );
if( !key_ptr )
{
return -1;
}
value_ptr = validate_value( self, value_ptr.get() );
if( !value_ptr )
{
return -1;
}
}
return PyDict_Type.tp_as_mapping->mp_ass_subscript( pyobject_cast( self ), key_ptr.get(), value_ptr.get() );
}
PyObject* AtomDict_setdefault( AtomDict* self, PyObject* args )
{
PyObject* key;
PyObject* dfv = Py_None;
if( !PyArg_UnpackTuple( args, "setdefault", 1, 2, &key, &dfv ) )
{
return 0;
}
PyObject* value = PyDict_GetItem( pyobject_cast( self ), key );
if( value )
{
return cppy::incref( value );
}
if( AtomDict_ass_subscript( self, key, dfv ) < 0 )
{
return 0;
}
// Get the dictionary from the dict itself in case it was coerced.
return cppy::incref( PyDict_GetItem( pyobject_cast( self ), key ) );
}
PyObject* AtomDict_update( AtomDict* dict, PyObject* args, PyObject* kwargs )
{
PyObject* item = 0;
if( !PyArg_UnpackTuple( args, "update", 0, 1, &item ) )
{
return 0;
}
if( !should_validate( dict ) )
{
if( merge_items( pyobject_cast( dict ), item, kwargs ) < 0 )
{
return 0;
}
else
{
return cppy::incref( Py_None );
}
}
cppy::ptr temp( PyDict_New() );
if( !temp )
{
return 0;
}
if( merge_items( temp.get(), item, kwargs ) < 0 )
{
return 0;
}
if( AtomDict::Update( dict, temp.get() ) < 0 )
{
return 0;
}
return cppy::incref( Py_None );
}
static PyMethodDef AtomDict_methods[] = {
{ "setdefault",
( PyCFunction )AtomDict_setdefault,
METH_VARARGS,
"D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D" },
{ "update",
( PyCFunction )AtomDict_update,
METH_VARARGS | METH_KEYWORDS,
"D.update([E, ]**F) -> None. Update D from dict/iterable E and F" },
{ 0 } // sentinel
};
static PyType_Slot AtomDict_Type_slots[] = {
{ Py_tp_dealloc, void_cast( AtomDict_dealloc ) }, /* tp_dealloc */
{ Py_mp_ass_subscript, void_cast( AtomDict_ass_subscript ) }, /* mp_ass_subscript */
{ Py_tp_traverse, void_cast( AtomDict_traverse ) }, /* tp_traverse */
{ Py_tp_clear, void_cast( AtomDict_clear ) }, /* tp_clear */
{ Py_tp_methods, void_cast( AtomDict_methods ) }, /* tp_methods */
{ Py_tp_base, void_cast( &PyDict_Type ) }, /* tp_base */
{ Py_tp_new, void_cast( AtomDict_new ) }, /* tp_new */
{ 0, 0 },
};
// DefaultAtomDict
int DefaultAtomDict_clear( DefaultAtomDict* self )
{
Py_CLEAR( self->factory );
return AtomDict_clear( atomdict_cast( self ) );
}
int DefaultAtomDict_traverse( DefaultAtomDict* self, visitproc visit, void* arg )
{
Py_VISIT( self->factory );
return AtomDict_traverse( atomdict_cast( self ), visit, arg );
}
void DefaultAtomDict_dealloc( DefaultAtomDict* self )
{
cppy::clear( &self->factory );
AtomDict_dealloc( atomdict_cast( self ) );
}
static PyObject* DefaultAtomDict_repr( DefaultAtomDict* self )
{
std::ostringstream ostr;
ostr << "defaultdict(";
cppy::ptr repr( PyObject_Repr( pyobject_cast( self->factory ) ) );
if( !repr )
{
return 0;
}
ostr << PyUnicode_AsUTF8( repr.get() );
ostr << ", ";
repr = PyDict_Type.tp_repr( pyobject_cast( self ) );
if( !repr )
{
return 0;
}
ostr << PyUnicode_AsUTF8( repr.get() );
ostr << ")";
return PyUnicode_FromString( ostr.str().c_str() );
}
static PyObject* DefaultAtomDict_missing( DefaultAtomDict* self, PyObject* args )
{
PyObject* key;
if( !PyArg_UnpackTuple( args, "__missing__", 1, 1, &key ) )
{
return 0;
}
CAtom* atom = self->dict.pointer->data();
if( !atom )
{
return cppy::runtime_error(
"Atom object to which this default dict is not alive anymore, "
"so missing value cannot be built."
);
}
#if PY_VERSION_HEX >= 0x03090000
cppy::ptr value_ptr( PyObject_CallOneArg( self->factory, pyobject_cast( atom ) ) );
#else
cppy::ptr temp( PyTuple_Pack(1, pyobject_cast( atom ) ) );
cppy::ptr value_ptr( PyObject_Call( self->factory, temp.get(), 0 ) );
#endif
if( !value_ptr )
{
return 0;
}
if( should_validate( atomdict_cast( self ) ) )
{
// We cannot simply validate the value as it will be re-validated when
// it is set which leads to creating a different object.
if( AtomDict_ass_subscript( atomdict_cast( self ), key, value_ptr.get() ) < 0 )
{
return 0;
}
// Get the dictionary from the dict itself in case it was coerced.
value_ptr = cppy::incref( PyDict_GetItem( pyobject_cast( self ), key ) );
}
return value_ptr.release();
}
static PyMethodDef DefaultAtomDict_methods[] = {
{ "__missing__",
( PyCFunction )DefaultAtomDict_missing,
METH_VARARGS,
"Called when a key is absent from the dictionary" },
{ 0 } // sentinel
};
static PyType_Slot DefaultAtomDict_Type_slots[] = {
{ Py_tp_dealloc, void_cast( DefaultAtomDict_dealloc ) }, /* tp_dealloc */
{ Py_tp_traverse, void_cast( DefaultAtomDict_traverse ) }, /* tp_traverse */
{ Py_tp_clear, void_cast( DefaultAtomDict_clear ) }, /* tp_clear */
{ Py_tp_repr, void_cast( DefaultAtomDict_repr ) }, /* tp_repr */
{ Py_tp_methods, void_cast( DefaultAtomDict_methods ) }, /* tp_methods */
/* tp_base cannot be set at this stage */
{ 0, 0 },
};
} // namespace
// Initialize static variables (otherwise the compiler eliminates them)
PyTypeObject* AtomDict::TypeObject = NULL;
PyType_Spec AtomDict::TypeObject_Spec = {
PACKAGE_TYPENAME( "atomdict" ), /* tp_name */
sizeof( AtomDict ), /* tp_basicsize */
0, /* tp_itemsize */
Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_BASETYPE
| Py_TPFLAGS_HAVE_GC
| Py_TPFLAGS_HAVE_VERSION_TAG, /* tp_flags */
AtomDict_Type_slots /* slots */
};
PyObject* AtomDict::New( CAtom* atom, Member* key_validator, Member* value_validator )
{
cppy::ptr self( PyDict_Type.tp_new( AtomDict::TypeObject, 0, 0 ) );
if( !self )
{
return 0;
}
cppy::xincref( pyobject_cast( key_validator ) );
atomdict_cast( self.get() )->m_key_validator = key_validator;
cppy::xincref( pyobject_cast( value_validator ) );
atomdict_cast( self.get() )->m_value_validator = value_validator;
atomdict_cast( self.get() )->pointer = new CAtomPointer( atom );
return self.release();
}
int AtomDict::Update( AtomDict* dict, PyObject* value )
{
cppy::ptr validated_dict( PyDict_New() );
PyObject* key;
PyObject* val;
Py_ssize_t index = 0;
while( PyDict_Next( value, &index, &key, &val ) )
{
cppy::ptr key_ptr( cppy::incref( key ) );
key_ptr = validate_key( dict, key_ptr.get() );
if( !key_ptr )
{
return -1;
}
cppy::ptr val_ptr( cppy::incref( val ) );
val_ptr = validate_value( dict, val_ptr.get() );
if( !val_ptr )
{
return -1;
}
if( PyDict_SetItem( validated_dict.get(), key_ptr.get(), val_ptr.get() ) != 0 )
{
return -1;
}
}
if( PyDict_Update( pyobject_cast( dict ), validated_dict.get() ) < 0 )
{
return -1;
}
return 0;
}
bool AtomDict::Ready()
{
// The reference will be handled by the module to which we will add the type
TypeObject = pytype_cast( PyType_FromSpec( &TypeObject_Spec ) );
if( !TypeObject )
{
return false;
}
return true;
}
// Initialize static variables (otherwise the compiler eliminates them)
PyTypeObject* DefaultAtomDict::TypeObject = NULL;
PyType_Spec DefaultAtomDict::TypeObject_Spec = {
PACKAGE_TYPENAME( "defaultatomdict" ), /* tp_name */
sizeof( DefaultAtomDict ), /* tp_basicsize */
0, /* tp_itemsize */
Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_BASETYPE
| Py_TPFLAGS_HAVE_GC
| Py_TPFLAGS_HAVE_VERSION_TAG, /* tp_flags */
DefaultAtomDict_Type_slots /* slots */
};
PyObject* DefaultAtomDict::New( CAtom* atom, Member* key_validator, Member* value_validator, PyObject* factory)
{
cppy::ptr self( PyDict_Type.tp_new( DefaultAtomDict::TypeObject, 0, 0 ) );
if( !self )
{
return 0; // LCOV_EXCL_LINE (failed instance creation)
}
cppy::xincref( pyobject_cast( key_validator ) );
atomdict_cast( self.get() )->m_key_validator = key_validator;
cppy::xincref( pyobject_cast( value_validator ) );
atomdict_cast( self.get() )->m_value_validator = value_validator;
atomdict_cast( self.get() )->pointer = new CAtomPointer( atom );
cppy::incref( pyobject_cast( factory ) );
// XXX validate we do get a callable taking 0 arg
defaultatomdict_cast( self.get() )->factory = factory;
return self.release();
}
bool DefaultAtomDict::Ready()
{
// This will work only if we create this type after the standard AtomDict
// The reference will be handled by the module to which we will add the type
PyObject* bases = PyTuple_New( 1 );
PyTuple_SET_ITEM( bases, 0, pyobject_cast( AtomDict::TypeObject ) );
TypeObject = pytype_cast(
PyType_FromSpecWithBases( &TypeObject_Spec, bases )
);
if( !TypeObject )
{
return false; // LCOV_EXCL_LINE (failed type creation)
}
return true;
}
} // namespace atom
atom-0.12.1/atom/src/atomdict.h 0000664 0000000 0000000 00000003073 15067567316 0016262 0 ustar 00root root 0000000 0000000 /*-----------------------------------------------------------------------------
| Copyright (c) 2019-2024, Nucleic
|
| Distributed under the terms of the BSD 3-Clause License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#pragma once
#include
#include "catom.h"
#include "catompointer.h"
#include "member.h"
#define atomdict_cast( o ) ( reinterpret_cast( o ) )
#define defaultatomdict_cast( o ) ( reinterpret_cast( o ) )
namespace atom
{
// POD struct - all member fields are considered private
struct AtomDict
{
PyDictObject dict;
Member* m_key_validator;
Member* m_value_validator;
CAtomPointer* pointer;
static PyType_Spec TypeObject_Spec;
static PyTypeObject* TypeObject;
static bool Ready();
static PyObject* New( CAtom* atom, Member* key_validator, Member* value_validator );
static int Update( AtomDict* dict, PyObject* value );
static bool TypeCheck( PyObject* ob )
{
return PyObject_TypeCheck( ob, TypeObject ) != 0;
}
};
// POD struct - all member fields are considered private
struct DefaultAtomDict
{
AtomDict dict;
PyObject* factory;
static PyType_Spec TypeObject_Spec;
static PyTypeObject* TypeObject;
static bool Ready();
static PyObject* New(
CAtom* atom, Member* key_validator, Member* value_validator, PyObject* factory
);
static bool TypeCheck( PyObject* ob )
{
return PyObject_TypeCheck( ob, TypeObject ) != 0;
}
};
} // namespace atom
atom-0.12.1/atom/src/atomlist.cpp 0000664 0000000 0000000 00000107221 15067567316 0016645 0 ustar 00root root 0000000 0000000 /*-----------------------------------------------------------------------------
| Copyright (c) 2013-2025, Nucleic Development Team.
|
| Distributed under the terms of the Modified BSD License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#include
#include "atomlist.h"
#include "packagenaming.h"
#ifdef __clang__
#pragma clang diagnostic ignored "-Wdeprecated-writable-strings"
#endif
#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wwrite-strings"
#endif
namespace atom
{
typedef PyCFunction pycfunc;
typedef PyCFunctionWithKeywords pycfunc_kw;
typedef _PyCFunctionFast pycfunc_f;
typedef _PyCFunctionFastWithKeywords pycfunc_fkw;
namespace ListMethods
{
pycfunc extend = 0;
static pycfunc_f pop = 0;
static pycfunc remove = 0;
inline PyCFunction
lookup_method( PyTypeObject* type, const char* name )
{
PyMethodDef* method = type->tp_methods;
for( ; method->ml_name != 0; ++method )
{
if( strcmp( method->ml_name, name ) == 0 )
return method->ml_meth;
}
return 0; // LCOV_EXCL_LINE (failed method lookup)
}
static bool
init_methods()
{
extend = lookup_method( &PyList_Type, "extend" );
if( !extend )
{
// LCOV_EXCL_START
cppy::system_error( "failed to load list 'extend' method" );
return false;
// LCOV_EXCL_STOP
}
pop = reinterpret_cast( lookup_method( &PyList_Type, "pop" ) );
if( !pop )
{
// LCOV_EXCL_START
cppy::system_error( "failed to load list 'pop' method" );
return false;
// LCOV_EXCL_STOP
}
remove = lookup_method( &PyList_Type, "remove" );
if( !remove )
{
// LCOV_EXCL_START
cppy::system_error( "failed to load list 'remove' method" );
return false;
// LCOV_EXCL_STOP
}
return true;
}
} // namespace ListMethods
PyObject*
ListSubtype_New( PyTypeObject* subtype, Py_ssize_t size )
{
if( size < 0 )
return cppy::system_error( "negative list size" );
if( static_cast( size ) > PY_SSIZE_T_MAX / sizeof( PyObject* ) )
return PyErr_NoMemory(); // LCOV_EXCL_LINE (memory error)
cppy::ptr ptr( PyType_GenericNew( subtype, 0, 0 ) );
if( !ptr )
return 0; // LCOV_EXCL_LINE (failed instance creation)
PyListObject* op = reinterpret_cast( ptr.get() );
if( size > 0 )
{
size_t nbytes = size * sizeof( PyObject* );
op->ob_item = reinterpret_cast( PyMem_Malloc( nbytes ) );
if( !op->ob_item )
return PyErr_NoMemory(); // LCOV_EXCL_LINE (memory error)
memset( op->ob_item, 0, nbytes );
}
#if PY_VERSION_HEX >= 0x03090000
Py_SET_SIZE( op, size );
#else
Py_SIZE( op ) = size;
#endif
op->allocated = size;
return ptr.release();
}
/*-----------------------------------------------------------------------------
| AtomList Type
|----------------------------------------------------------------------------*/
namespace
{
class AtomListHandler
{
public:
AtomListHandler( AtomList* list ) :
m_list( cppy::incref( pyobject_cast( list ) ) ) {}
PyObject* append( PyObject* value )
{
cppy::ptr item( validate_single( value ) );
if( !item )
return 0;
if( PyList_Append( m_list.get(), item.get() ) != 0 )
{
return 0; // LCOV_EXCL_LINE (failed append, impossible)
}
return cppy::incref( Py_None );
}
PyObject* insert( PyObject* args )
{
Py_ssize_t index;
PyObject* value;
if( !PyArg_ParseTuple( args, "nO:insert", &index, &value ) )
return 0;
cppy::ptr valptr( validate_single( value ) );
if( !valptr )
return 0;
if( PyList_Insert( m_list.get(), index, valptr.get() ) != 0)
{
return 0; // LCOV_EXCL_LINE (failed insert, impossible)
}
return cppy::incref( Py_None );
}
PyObject* extend( PyObject* value )
{
cppy::ptr item( validate_sequence( value ) );
if( !item )
return 0;
return ListMethods::extend( m_list.get(), item.get() );
}
PyObject* iadd( PyObject* value )
{
cppy::ptr item( validate_sequence( value ) );
if( !item )
return 0;
return PyList_Type.tp_as_sequence->sq_inplace_concat(
m_list.get(), item.get() );
}
int setitem( Py_ssize_t index, PyObject* value )
{
if( !value )
return PyList_Type.tp_as_sequence->sq_ass_item(
m_list.get(), index, value );
cppy::ptr item( validate_single( value ) );
if( !item )
return -1;
return PyList_Type.tp_as_sequence->sq_ass_item(
m_list.get(), index, item.get() );
}
int setitem( PyObject* key, PyObject* value )
{
if( !value )
return PyList_Type.tp_as_mapping->mp_ass_subscript(
m_list.get(), key, value );
cppy::ptr item;
if( PyIndex_Check( key ) )
item = validate_single( value );
else if( PySlice_Check( key ) )
item = validate_sequence( value );
else
item = cppy::incref( value );
if( !item )
return -1;
return PyList_Type.tp_as_mapping->mp_ass_subscript(
m_list.get(), key, item.get() );
}
protected:
AtomList* alist()
{
return atomlist_cast( m_list.get() );
}
Member* validator()
{
return alist()->validator;
}
CAtom* atom()
{
return alist()->pointer->data();
}
PyObject* validate_single( PyObject* value )
{
cppy::ptr item( cppy::incref( value ) );
if( validator() && atom() )
{
item = validator()->full_validate( atom(), Py_None, item.get() );
if( !item )
return 0;
}
m_validated = item;
return item.release();
}
PyObject* validate_sequence( PyObject* value )
{
cppy::ptr item( cppy::incref( value ) );
if( validator() && atom() )
{
// no validation needed for self[::-1] = self
if( m_list.get() != value )
{
cppy::ptr templist( PySequence_List( value ) );
if( !templist )
return 0;
CAtom* atm = atom();
Member* vd = validator();
Py_ssize_t size = PyList_GET_SIZE( templist.get() );
for( Py_ssize_t i = 0; i < size; ++i )
{
// Borrow a reference to an item in the list
PyObject* b = PyList_GET_ITEM( templist.get(), i );
PyObject* val = vd->full_validate( atm, Py_None, b );
if( !val )
return 0;
PyList_SET_ITEM( templist.get(), i, cppy::incref( val ) );
}
item = templist;
}
}
m_validated = item;
return item.release();
}
cppy::ptr m_list;
cppy::ptr m_validated;
private:
AtomListHandler();
};
PyObject*
AtomList_new( PyTypeObject* type, PyObject* args, PyObject* kwargs )
{
cppy::ptr ptr( PyList_Type.tp_new( type, args, kwargs ) );
if( !ptr )
{
return 0; // LCOV_EXCL_LINE (failed instance creation)
}
atomlist_cast( ptr.get() )->pointer = new CAtomPointer();
return ptr.release();
}
int AtomList_clear( AtomList* self )
{
Py_CLEAR( self->validator );
return PyList_Type.tp_clear( pyobject_cast( self ) );
}
int AtomList_traverse( AtomList* self, visitproc visit, void* arg )
{
Py_VISIT( self->validator );
#if PY_VERSION_HEX >= 0x03090000
// This was not needed before Python 3.9 (Python issue 35810 and 40217)
Py_VISIT(Py_TYPE(self));
#endif
// PyList_type is not heap allocated so it does visit the type
return PyList_Type.tp_traverse( pyobject_cast( self ), visit, arg );
}
void
AtomList_dealloc( AtomList* self )
{
PyObject_GC_UnTrack( self );
cppy::clear( &self->validator );
delete self->pointer;
self->pointer = 0;
PyList_Type.tp_dealloc( pyobject_cast( self ) );
}
PyObject*
AtomList_append( AtomList* self, PyObject* value )
{
return AtomListHandler( self ).append( value );
}
PyObject*
AtomList_insert( AtomList* self, PyObject* args )
{
return AtomListHandler( self ).insert( args );
}
PyObject*
AtomList_extend( AtomList* self, PyObject* value )
{
return AtomListHandler( self ).extend( value );
}
PyObject*
AtomList_reduce_ex( AtomList* self, PyObject* proto )
{
// An atomlist is pickled as a normal list. When the Atom class is
// reconstituted, assigning the list to the attribute will create
// a new atomlist with the proper owner. There is no need to try
// to persist the validator and pointer information.
cppy::ptr data( PySequence_List( pyobject_cast( self ) ) );
if( !data )
return 0;
cppy::ptr res( PyTuple_New( 2 ) );
if( !res )
return 0;
cppy::ptr args( PyTuple_New( 1 ) );
if( !args )
return 0;
PyTuple_SET_ITEM( args.get(), 0, data.release() );
PyTuple_SET_ITEM( res.get(), 0, cppy::incref( pyobject_cast( &PyList_Type ) ) );
PyTuple_SET_ITEM( res.get(), 1, args.release() );
return res.release();
}
int
AtomList_ass_item( AtomList* self, Py_ssize_t index, PyObject* value )
{
return AtomListHandler( self ).setitem( index, value );
}
PyObject*
AtomList_inplace_concat( AtomList* self, PyObject* value )
{
return AtomListHandler( self ).iadd( value );
}
int
AtomList_ass_subscript( AtomList* self, PyObject* key, PyObject* value )
{
return AtomListHandler( self ).setitem( key, value );
}
PyDoc_STRVAR( a_append_doc,
"L.append(object) -- append object to end" );
PyDoc_STRVAR( a_insert_doc,
"L.insert(index, object) -- insert object before index" );
PyDoc_STRVAR( a_extend_doc,
"L.extend(iterable) -- extend list by appending elements from the iterable" );
static PyMethodDef AtomList_methods[] = {
{ "append", ( PyCFunction )AtomList_append, METH_O, a_append_doc },
{ "insert", ( PyCFunction )AtomList_insert, METH_VARARGS, a_insert_doc },
{ "extend", ( PyCFunction )AtomList_extend, METH_O, a_extend_doc },
{ "__reduce_ex__", ( PyCFunction )AtomList_reduce_ex, METH_O, "" },
{ 0 } /* sentinel */
};
static PyType_Slot AtomList_Type_slots[] = {
{ Py_tp_base, void_cast( &PyList_Type ) }, /* tp_base */
{ Py_tp_new, void_cast( AtomList_new ) }, /* tp_new */
{ Py_tp_dealloc, void_cast( AtomList_dealloc ) }, /* tp_dealloc */
{ Py_tp_traverse, void_cast( AtomList_traverse ) }, /* tp_traverse */
{ Py_tp_clear, void_cast( AtomList_clear ) }, /* tp_clear */
{ Py_tp_methods, void_cast( AtomList_methods ) }, /* tp_methods */
{ Py_sq_ass_item, void_cast( AtomList_ass_item ) }, /* sq_ass_item */
{ Py_sq_inplace_concat, void_cast( AtomList_inplace_concat ) }, /* sq_ass_item */
{ Py_mp_ass_subscript, void_cast( AtomList_ass_subscript ) }, /* mp_ass_subscript */
{ 0, 0 },
};
} // namespace
PyTypeObject* AtomList::TypeObject = NULL;
PyType_Spec AtomList::TypeObject_Spec = {
PACKAGE_TYPENAME( "atomlist" ), /* tp_name */
sizeof( AtomList ), /* tp_basicsize */
0, /* tp_itemsize */
Py_TPFLAGS_DEFAULT
|Py_TPFLAGS_BASETYPE
|Py_TPFLAGS_HAVE_GC, /* tp_flags */
AtomList_Type_slots /* slots */
};
PyObject*
AtomList::New( Py_ssize_t size, CAtom* atom, Member* validator )
{
cppy::ptr ptr( ListSubtype_New( AtomList::TypeObject, size ) );
if( !ptr )
return 0;
cppy::xincref( pyobject_cast( validator ) );
atomlist_cast( ptr.get() )->validator = validator;
atomlist_cast( ptr.get() )->pointer = new CAtomPointer( atom );
return ptr.release();
}
bool AtomList::Ready()
{
if( !ListMethods::init_methods() ) {
return false; // LCOV_EXCL_LINE (failed method lookup, impossible)
}
// The reference will be handled by the module to which we will add the type
TypeObject = pytype_cast( PyType_FromSpec( &TypeObject_Spec ) );
if( !TypeObject )
{
return false; // LCOV_EXCL_LINE (failed type creation)
}
return true;
}
/*-----------------------------------------------------------------------------
| AtomCList Type
|----------------------------------------------------------------------------*/
namespace PySStr
{
static PyObject* typestr;
static PyObject* namestr;
static PyObject* objectstr;
static PyObject* valuestr ;
static PyObject* operationstr ;
static PyObject* itemstr ;
static PyObject* itemsstr ;
static PyObject* indexstr ;
static PyObject* keystr ;
static PyObject* reversestr ;
static PyObject* containerstr ;
static PyObject* __delitem__str ;
static PyObject* __iadd__str ;
static PyObject* __imul__str ;
static PyObject* __setitem__str ;
static PyObject* appendstr ;
static PyObject* extendstr ;
static PyObject* insertstr ;
static PyObject* popstr ;
static PyObject* removestr ;
static PyObject* sortstr ;
static PyObject* olditemstr ;
static PyObject* newitemstr ;
static PyObject* countstr ;
} // namespace PySStr
bool
init_containerlistchange()
{
static bool alloced = false;
if( alloced )
{
return true;
}
PySStr::typestr = PyUnicode_InternFromString( "type" );
if( !PySStr::typestr )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
PySStr::namestr = PyUnicode_InternFromString( "name" );
if( !PySStr::namestr )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
PySStr::objectstr = PyUnicode_InternFromString( "object" );
if( !PySStr::objectstr )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
PySStr::valuestr = PyUnicode_InternFromString( "value" );
if( !PySStr::valuestr )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
PySStr::operationstr = PyUnicode_InternFromString( "operation" );
if( !PySStr::operationstr )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
PySStr::itemstr = PyUnicode_InternFromString( "item" );
if( !PySStr::itemstr )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
PySStr::itemsstr = PyUnicode_InternFromString( "items" );
if( !PySStr::itemsstr )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
PySStr::indexstr = PyUnicode_InternFromString( "index" );
if( !PySStr::indexstr )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
PySStr::keystr = PyUnicode_InternFromString( "key" );
if( !PySStr::keystr )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
PySStr::reversestr = PyUnicode_InternFromString( "reverse" );
if( !PySStr::reversestr )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
PySStr::containerstr = PyUnicode_InternFromString( "container" );
if( !PySStr::containerstr )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
PySStr::__delitem__str = PyUnicode_InternFromString( "__delitem__" );
if( !PySStr::typestr )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
PySStr::__iadd__str = PyUnicode_InternFromString( "__iadd__" );
if( !PySStr::__iadd__str )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
PySStr::__imul__str = PyUnicode_InternFromString( "__imul__" );
if( !PySStr::__imul__str )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
PySStr::__setitem__str = PyUnicode_InternFromString( "__setitem__" );
if( !PySStr::__setitem__str )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
PySStr::appendstr = PyUnicode_InternFromString( "append" );
if( !PySStr::appendstr )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
PySStr::extendstr = PyUnicode_InternFromString( "extend" );
if( !PySStr::extendstr )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
PySStr::insertstr = PyUnicode_InternFromString( "insert" );
if( !PySStr::insertstr )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
PySStr::popstr = PyUnicode_InternFromString( "pop" );
if( !PySStr::popstr )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
PySStr::removestr = PyUnicode_InternFromString( "remove" );
if( !PySStr::removestr )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
PySStr::sortstr = PyUnicode_InternFromString( "sort" );
if( !PySStr::sortstr )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
PySStr::olditemstr = PyUnicode_InternFromString( "olditem" );
if( !PySStr::olditemstr )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
PySStr::newitemstr = PyUnicode_InternFromString( "newitem" );
if( !PySStr::newitemstr )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
PySStr::countstr = PyUnicode_InternFromString( "count" );
if( !PySStr::countstr )
{
return false; // LCOV_EXCL_LINE (failed interned string creation)
}
alloced = true;
return true;
}
namespace
{
class AtomCListHandler : public AtomListHandler
{
static void clip_index( Py_ssize_t& index, Py_ssize_t size )
{
if( index < 0 )
{
index += size;
if( index < 0 )
index = 0;
}
if( index > size )
index = size;
}
// XXX should I add clear ?
public:
AtomCListHandler( AtomCList* list ) :
AtomListHandler( atomlist_cast( list ) ),
m_obsm( false ), m_obsa( false ) {}
PyObject* append( PyObject* value )
{
cppy::ptr res( AtomListHandler::append( value ) );
if( !res )
return 0;
if( observer_check() )
{
cppy::ptr c( prepare_change() );
if( !c )
return 0; // LCOV_EXCL_LINE
if( PyDict_SetItem( c.get(), PySStr::operationstr, PySStr::appendstr ) != 0 )
return 0;
if( PyDict_SetItem( c.get(), PySStr::itemstr, m_validated.get() ) != 0 )
return 0;
if( !post_change( c ) )
return 0;
}
return res.release();
}
PyObject* insert( PyObject* args )
{
Py_ssize_t size = PyList_GET_SIZE( m_list.get() );
cppy::ptr res( AtomListHandler::insert( args ) );
if( !res )
return 0;
if( observer_check() )
{
cppy::ptr c( prepare_change() );
if( !c )
return 0; // LCOV_EXCL_LINE
if( PyDict_SetItem( c.get(), PySStr::operationstr, PySStr::insertstr ) != 0 )
return 0;
// if the superclass call succeeds, then this is safe.
Py_ssize_t where = PyLong_AsSsize_t( PyTuple_GET_ITEM( args, 0 ) );
clip_index( where, size );
cppy::ptr index( PyLong_FromSsize_t( where ) );
if( PyDict_SetItem( c.get(), PySStr::indexstr, index.get() ) != 0 )
return 0;
if( PyDict_SetItem( c.get(), PySStr::itemstr, m_validated.get() ) != 0)
return 0;
if( !post_change( c ) )
return 0;
}
return res.release();
}
PyObject* extend( PyObject* value )
{
cppy::ptr res( AtomListHandler::extend( value ) );
if( !res )
return 0;
if( observer_check() )
{
cppy::ptr c( prepare_change() );
if( !c )
return 0; // LCOV_EXCL_LINE
if( PyDict_SetItem( c.get(), PySStr::operationstr, PySStr::extendstr ) != 0 )
return 0;
if( PyDict_SetItem( c.get(), PySStr::itemsstr, m_validated.get() ) != 0 )
return 0;
if( !post_change( c ) )
return 0;
}
return res.release();
}
PyObject* pop( PyObject* args )
{
Py_ssize_t size = PyList_GET_SIZE( m_list.get() );
int nargs = (int)PyTuple_GET_SIZE( args);
PyObject **stack = &PyTuple_GET_ITEM(args, 0);
cppy::ptr res( ListMethods::pop( m_list.get(), stack, nargs ) );
if( !res )
return 0;
if( observer_check() )
{
cppy::ptr c( prepare_change() );
if( !c )
return 0; // LCOV_EXCL_LINE
if( PyDict_SetItem( c.get(), PySStr::operationstr, PySStr::popstr ) != 0 )
return 0;
// if the superclass call succeeds, then this is safe.
Py_ssize_t i = -1;
if( PyTuple_GET_SIZE( args ) == 1 )
i = PyLong_AsSsize_t( PyTuple_GET_ITEM( args, 0 ) );
if( i < 0 )
i += size;
cppy::ptr index( PyLong_FromSsize_t( i ) );
if( PyDict_SetItem( c.get(), PySStr::indexstr, index.get() ) != 0 )
return 0;
if( PyDict_SetItem( c.get(), PySStr::itemstr, res.get() ) != 0 )
return 0;
if( !post_change( c ) )
return 0;
}
return res.release();
}
PyObject* remove( PyObject* value )
{
cppy::ptr res( ListMethods::remove( m_list.get(), value ) );
if( !res )
return 0;
if( observer_check() )
{
cppy::ptr c( prepare_change() );
if( !c )
return 0; // LCOV_EXCL_LINE
if( PyDict_SetItem( c.get(), PySStr::operationstr, PySStr::removestr ) != 0)
return 0;
if( PyDict_SetItem( c.get(), PySStr::itemstr, value ) != 0 )
return 0;
if( !post_change( c ) )
return 0;
}
return res.release();
}
PyObject* reverse()
{
int res( PyList_Reverse( m_list.get() ) );
if( res != 0 )
return 0;
if( observer_check() )
{
cppy::ptr c( prepare_change() );
if( !c )
return 0; // LCOV_EXCL_LINE
if( PyDict_SetItem( c.get(), PySStr::operationstr, PySStr::reversestr ) != 0)
return 0;
if( !post_change( c ) )
return 0;
}
return cppy::incref( Py_None );
}
PyObject* sort( PyObject* args, PyObject* kwargs )
{
static char *kwlist[] = { "key", "reverse", 0 };
// Get a reference to builtins (borrowed ref hence the incref)
cppy::ptr builtins( cppy::xincref( PyImport_AddModule("builtins") ) );
if ( !builtins )
return 0;
cppy::ptr super_type( builtins.getattr( "super" ) );
if ( !super_type )
return 0;
// Create super args (tuple steals references)
cppy::ptr super_args( PyTuple_New(2) );
if ( !super_args )
return 0;
PyTuple_SET_ITEM( super_args.get(), 0, cppy::incref( pyobject_cast( Py_TYPE(m_list.get()) ) ) );
PyTuple_SET_ITEM( super_args.get(), 1, cppy::incref( m_list.get() ) );
// Get and call super method
cppy::ptr super( super_type.call( super_args ) );
if ( !super )
return 0;
cppy::ptr meth( super.getattr( "sort" ) );
if ( !meth )
return 0;
cppy::ptr res( meth.call(args, kwargs) );
if( !res )
return 0;
if( observer_check() )
{
cppy::ptr c( prepare_change() );
if( !c )
return 0; // LCOV_EXCL_LINE
if( PyDict_SetItem( c.get(), PySStr::operationstr, PySStr::sortstr ) != 0 )
return 0;
PyObject* key = Py_None;
int rev = 0;
if( !PyArg_ParseTupleAndKeywords(
args, kwargs, "|Oi", kwlist, &key, &rev ) )
return 0;
if( PyDict_SetItem( c.get(), PySStr::keystr, key ) != 0)
return 0;
if( PyDict_SetItem( c.get(), PySStr::reversestr, rev ? Py_True : Py_False ) != 0 )
return 0;
if( !post_change( c ) )
return 0;
}
return res.release();
}
PyObject* iadd( PyObject* value )
{
cppy::ptr res( AtomListHandler::iadd( value ) );
if( !res )
return 0;
if( observer_check() )
{
cppy::ptr c( prepare_change() );
if( !c )
return 0; // LCOV_EXCL_LINE
if( PyDict_SetItem( c.get(), PySStr::operationstr, PySStr::__iadd__str ) != 0 )
return 0;
if( PyDict_SetItem( c.get(), PySStr::itemsstr, m_validated.get() ) != 0 )
return 0;
if( !post_change( c ) )
return 0;
}
return res.release();
}
PyObject* imul( Py_ssize_t count )
{
cppy::ptr res( PyList_Type.tp_as_sequence->sq_inplace_repeat(
m_list.get(), count ) );
if( !res )
return 0;
if( observer_check() )
{
cppy::ptr c( prepare_change() );
if( !c )
return 0; // LCOV_EXCL_LINE
if( PyDict_SetItem( c.get(), PySStr::operationstr, PySStr::__imul__str ) != 0 )
return 0;
cppy::ptr pycount( PyLong_FromSsize_t( count ) );
if( !pycount )
return 0;
if( PyDict_SetItem( c.get(), PySStr::countstr, pycount.get() ) != 0 )
return 0;
if( !post_change( c ) )
return 0;
}
return res.release();
}
int setitem( Py_ssize_t index, PyObject* value )
{
cppy::ptr olditem;
bool obs = observer_check();
if( obs )
{
olditem = cppy::xincref(PyList_GetItem( m_list.get(), index ));
if( !olditem )
return -1;
}
int res = AtomListHandler::setitem( index, value );
if( res < 0 )
return res;
if( obs )
{
cppy::ptr pyindex( PyLong_FromSsize_t( index ) );
if( !pyindex )
return -1;
res = post_setitem_change( pyindex, olditem, m_validated );
}
return res;
}
int setitem( PyObject* key, PyObject* value )
{
cppy::ptr olditem;
bool obs = observer_check();
if( obs )
{
olditem = PyObject_GetItem( m_list.get(), key );
if( !olditem )
return -1;
}
int res = AtomListHandler::setitem( key, value );
if( res < 0 )
return res;
if( obs )
{
cppy::ptr index( cppy::incref( key ) );
res = post_setitem_change( index, olditem, m_validated );
}
return res;
}
private:
AtomCListHandler();
AtomCList* clist()
{
return atomclist_cast( m_list.get() );
}
Member* member()
{
return clist()->member;
}
bool observer_check()
{
m_obsm = false;
m_obsa = false;
if( !member() || !atom() )
return false;
m_obsm = member()->has_observers( ChangeType::Container );
m_obsa = atom()->has_observers( member()->name );
return m_obsm || m_obsa;
}
PyObject* prepare_change()
{
cppy::ptr c( PyDict_New() );
if( !c )
return 0;
if( PyDict_SetItem( c.get(), PySStr::typestr, PySStr::containerstr ) != 0 )
return 0;
if( PyDict_SetItem( c.get(), PySStr::namestr, member()->name ) != 0 )
return 0;
if( PyDict_SetItem( c.get(), PySStr::objectstr, pyobject_cast( atom() ) ) != 0 )
return 0;
if( PyDict_SetItem( c.get(), PySStr::valuestr, m_list.get() ) != 0 )
return 0;
return c.release();
}
bool post_change( cppy::ptr& change )
{
cppy::ptr args( PyTuple_New( 1 ) );
if( !args )
return false;
PyTuple_SET_ITEM( args.get(), 0, change.release() );
if( m_obsm )
{
if( !member()->notify( atom(), args.get(), 0, ChangeType::Container ) )
return false;
}
if( m_obsa )
{
if( !atom()->notify( member()->name, args.get(), 0, ChangeType::Container ) )
return false;
}
return true;
}
int post_setitem_change( cppy::ptr& i, cppy::ptr& o, cppy::ptr& n )
{
cppy::ptr c( prepare_change() );
if( !c )
return -1;
if( n )
{
if( PyDict_SetItem( c.get(), PySStr::operationstr, PySStr::__setitem__str ) != 0 )
return -1;
if( PyDict_SetItem( c.get(), PySStr::olditemstr, o.get() ) != 0)
return -1;
if( PyDict_SetItem( c.get(), PySStr::newitemstr, n.get() ) != 0)
return -1;
}
else
{
if( PyDict_SetItem( c.get(), PySStr::operationstr, PySStr::__delitem__str ) != 0 )
return -1;
if( PyDict_SetItem( c.get(), PySStr::itemstr, o.get() ) != 0 )
return -1;
}
if( PyDict_SetItem( c.get(), PySStr::indexstr, i.get() ) != 0 )
return -1;
if( !post_change( c ) )
return -1;
return 0;
}
bool m_obsm;
bool m_obsa;
};
PyObject*
AtomCList_new( PyTypeObject* type, PyObject* args, PyObject* kwargs )
{
return AtomList::TypeObject->tp_new( type, args, kwargs );
}
int AtomCList_clear( AtomCList* self )
{
Py_CLEAR( self->member );
return AtomList_clear( atomlist_cast( self ) );
}
int AtomCList_traverse( AtomCList* self, visitproc visit, void* arg )
{
Py_VISIT( self->member );
return AtomList_traverse( atomlist_cast( self ) , visit, arg );
}
void
AtomCList_dealloc( AtomCList* self )
{
PyObject_GC_UnTrack( self );
cppy::clear( &self->member );
cppy::clear( &atomlist_cast( self )->validator );
delete atomlist_cast( self )->pointer;
atomlist_cast( self )->pointer = 0;
PyList_Type.tp_dealloc( pyobject_cast( self ) );
}
PyObject*
AtomCList_append( AtomCList* self, PyObject* value )
{
return AtomCListHandler( self ).append( value );
}
PyObject*
AtomCList_insert( AtomCList* self, PyObject* args )
{
return AtomCListHandler( self ).insert( args );
}
PyObject*
AtomCList_extend( AtomCList* self, PyObject* value )
{
return AtomCListHandler( self ).extend( value );
}
PyObject*
AtomCList_pop( AtomCList* self, PyObject* args )
{
return AtomCListHandler( self ).pop( args );
}
PyObject*
AtomCList_remove( AtomCList* self, PyObject* value )
{
return AtomCListHandler( self ).remove( value );
}
PyObject*
AtomCList_reverse( AtomCList* self )
{
return AtomCListHandler( self ).reverse();
}
PyObject*
AtomCList_sort( AtomCList* self, PyObject* args, PyObject* kwargs )
{
return AtomCListHandler( self ).sort( args, kwargs );
}
int
AtomCList_ass_item( AtomCList* self, Py_ssize_t index, PyObject* value )
{
return AtomCListHandler( self ).setitem( index, value );
}
PyObject*
AtomCList_inplace_concat( AtomCList* self, PyObject* value )
{
return AtomCListHandler( self ).iadd( value );
}
PyObject*
AtomCList_inplace_repeat( AtomCList* self, Py_ssize_t count )
{
return AtomCListHandler( self ).imul( count );
}
int
AtomCList_ass_subscript( AtomCList* self, PyObject* key, PyObject* value )
{
return AtomCListHandler( self ).setitem( key, value );
}
PyDoc_STRVAR(c_append_doc,
"L.append(object) -- append object to end");
PyDoc_STRVAR(c_insert_doc,
"L.insert(index, object) -- insert object before index");
PyDoc_STRVAR(c_extend_doc,
"L.extend(iterable) -- extend list by appending elements from the iterable");
PyDoc_STRVAR(c_pop_doc,
"L.pop([index]) -> item -- remove and return item at index (default last).\n"
"Raises IndexError if list is empty or index is out of range.");
PyDoc_STRVAR(c_remove_doc,
"L.remove(value) -- remove first occurrence of value.\n"
"Raises ValueError if the value is not present.");
PyDoc_STRVAR(c_reverse_doc,
"L.reverse() -- reverse *IN PLACE*");
PyDoc_STRVAR(c_sort_doc,
"L.sort(cmp=None, key=None, reverse=False) -- stable sort *IN PLACE*;\n\
cmp(x, y) -> -1, 0, 1");
static PyMethodDef
AtomCList_methods[] = {
{ "append", ( PyCFunction )AtomCList_append, METH_O, c_append_doc },
{ "insert", ( PyCFunction )AtomCList_insert, METH_VARARGS, c_insert_doc },
{ "extend", ( PyCFunction )AtomCList_extend, METH_O, c_extend_doc },
{ "pop", ( PyCFunction )AtomCList_pop, METH_VARARGS, c_pop_doc },
{ "remove", ( PyCFunction )AtomCList_remove, METH_O, c_remove_doc },
{ "reverse", ( PyCFunction )AtomCList_reverse, METH_NOARGS, c_reverse_doc },
{ "sort", ( PyCFunction )AtomCList_sort, METH_VARARGS | METH_KEYWORDS, c_sort_doc },
{ 0 } /* sentinel */
};
static PyType_Slot AtomCList_Type_slots[] = {
{ Py_tp_base, NULL }, // Set once the base type is created /* tp_base */
{ Py_tp_new, void_cast( AtomCList_new ) }, /* tp_new */
{ Py_tp_dealloc, void_cast( AtomCList_dealloc ) }, /* tp_dealloc */
{ Py_tp_traverse, void_cast( AtomCList_traverse ) }, /* tp_traverse */
{ Py_tp_clear, void_cast( AtomCList_clear ) }, /* tp_clear */
{ Py_tp_methods, void_cast( AtomCList_methods ) }, /* tp_methods */
{ Py_sq_ass_item, void_cast( AtomCList_ass_item ) }, /* sq_ass_item */
{ Py_sq_inplace_concat, void_cast( AtomCList_inplace_concat ) }, /* sq_ass_item */
{ Py_sq_inplace_repeat, void_cast( AtomCList_inplace_repeat ) }, /* sq_ass_item */
{ Py_mp_ass_subscript, void_cast( AtomCList_ass_subscript ) }, /* mp_ass_subscript */
{ 0, 0 },
};
} // namespace
PyTypeObject* AtomCList::TypeObject = NULL;
PyType_Spec AtomCList::TypeObject_Spec = {
PACKAGE_TYPENAME( "atomclist" ), /* tp_name */
sizeof( AtomCList ), /* tp_basicsize */
0, /* tp_itemsize */
Py_TPFLAGS_DEFAULT
|Py_TPFLAGS_BASETYPE
|Py_TPFLAGS_HAVE_GC, /* tp_flags */
AtomCList_Type_slots /* slots */
};
PyObject*
AtomCList::New( Py_ssize_t size, CAtom* atom, Member* validator, Member* member )
{
cppy::ptr ptr( ListSubtype_New( AtomCList::TypeObject, size ) );
if( !ptr )
return 0;
cppy::xincref( pyobject_cast( validator ) );
cppy::xincref( pyobject_cast( member ) );
atomlist_cast( ptr.get() )->validator = validator;
atomlist_cast( ptr.get() )->pointer = new CAtomPointer( atom );
atomclist_cast( ptr.get() )->member = member;
return ptr.release();
}
bool AtomCList::Ready()
{
// Ensure the parent type was created
if( !AtomList::TypeObject )
{
return false; // LCOV_EXCL_LINE (parent type not created, impossible)
}
AtomCList_Type_slots[0].pfunc = void_cast( AtomList::TypeObject );
// The reference will be handled by the module to which we will add the type
TypeObject = pytype_cast( PyType_FromSpec( &TypeObject_Spec ) );
if( !TypeObject )
{
return false; // LCOV_EXCL_LINE (failed type creation)
}
return true;
}
} // namespace atom
atom-0.12.1/atom/src/atomlist.h 0000664 0000000 0000000 00000003013 15067567316 0016304 0 ustar 00root root 0000000 0000000 /*-----------------------------------------------------------------------------
| Copyright (c) 2013-2025, Nucleic Development Team.
|
| Distributed under the terms of the Modified BSD License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#pragma once
#include
#include "catom.h"
#include "catompointer.h"
#include "member.h"
#define atomlist_cast( o ) ( reinterpret_cast( o ) )
#define atomclist_cast( o ) ( reinterpret_cast( o ) )
namespace atom
{
// POD struct - all member fields are considered private
struct AtomList
{
PyListObject list;
Member* validator;
CAtomPointer* pointer;
static PyType_Spec TypeObject_Spec;
static PyTypeObject* TypeObject;
static bool Ready();
static PyObject* New( Py_ssize_t size, CAtom* atom, Member* validator );
static bool TypeCheck( PyObject* ob )
{
return PyObject_TypeCheck( ob, TypeObject ) != 0;
}
};
bool
init_containerlistchange();
// POD struct - all member fields are considered private
struct AtomCList
{
PyListObject list;
Member* validator;
CAtomPointer* pointer;
Member* member;
static PyType_Spec TypeObject_Spec;
static PyTypeObject* TypeObject;
static bool Ready();
static PyObject* New( Py_ssize_t size, CAtom* atom, Member* validator, Member* member );
static bool TypeCheck( PyObject* ob )
{
return PyObject_TypeCheck( ob, TypeObject ) != 0;
}
};
}
atom-0.12.1/atom/src/atomref.cpp 0000664 0000000 0000000 00000011167 15067567316 0016451 0 ustar 00root root 0000000 0000000 /*-----------------------------------------------------------------------------
| Copyright (c) 2013-2025, Nucleic Development Team.
|
| Distributed under the terms of the Modified BSD License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#include