underpyx 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
underpyx-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Ariana Maghsoudi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ include README.md
2
+ include LICENSE
3
+ recursive-exclude tests *
@@ -0,0 +1,102 @@
1
+ Metadata-Version: 2.4
2
+ Name: underpyx
3
+ Version: 0.1.0
4
+ Summary: Fundamental utilities in python, such as encapsulation, immutability, service patterns, and JSON typing in Python.
5
+ Author-email: Ariana Maghsoudi <ariana.maghsoudi82@gmail.com>
6
+ License: MIT
7
+ Requires-Python: >=3.8
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Provides-Extra: dev
11
+ Requires-Dist: pytest; extra == "dev"
12
+ Requires-Dist: assertpy; extra == "dev"
13
+ Dynamic: license-file
14
+
15
+ # underpy ๐Ÿ
16
+
17
+ **Reusable Python base classes for clean, maintainable, and scalable projects.**
18
+ Underpy provides foundational building blocks such as encapsulated data, immutable objects, service patterns, and JSON typing โ€” so you can start every new project with a solid, consistent architecture.
19
+
20
+ ---
21
+
22
+ ## โœจ Features
23
+
24
+ - **Encapsulated** โ€“ Hide internals and expose clean public APIs
25
+ - **Immutable** โ€“ Prevent changes to objects after initialization
26
+ - **Service Class (Singleton)** โ€“ Enforce a single point of access for core services
27
+ - **JSON Type** โ€“ Typed JSON data handling made easy
28
+ - **Tested with Pytest + AssertPy** โ€“ Reliable and expressive tests
29
+
30
+ ---
31
+
32
+ ## ๐Ÿ“ฆ Installation
33
+
34
+ Install from source (local development):
35
+
36
+ ```bash
37
+ pip install git+https://github.com/ariana126/underpy.git
38
+ ```
39
+
40
+ ---
41
+
42
+ ## ๐Ÿš€ Quick Start
43
+
44
+ ### Example: Encapsulated
45
+ ```python
46
+ from underpy import Encapsulated
47
+
48
+ class User(Encapsulated):
49
+ def __init__(self, username):
50
+ self._username = username
51
+
52
+ def get_username(self):
53
+ return self._username
54
+
55
+ user = User("ariana")
56
+ print(user.get_username()) # โœ… "ariana"
57
+ # Direct access is avoided: user._username
58
+ ```
59
+
60
+ ---
61
+
62
+ ### Example: Immutable
63
+ ```python
64
+ from underpy import Immutable
65
+
66
+ class Config(Immutable):
67
+ def __init__(self, host, port):
68
+ self.host = host
69
+ self.port = port
70
+
71
+ cfg = Config("localhost", 8080)
72
+ # cfg.port = 9000 # โŒ Raises AttributeError
73
+ ```
74
+
75
+ ---
76
+
77
+ ## ๐Ÿงช Running Tests
78
+
79
+ ```bash
80
+ pytest
81
+ ```
82
+
83
+ ---
84
+
85
+ ## ๐Ÿ“„ License
86
+ This project is licensed under the **MIT License** โ€“ see the [LICENSE](LICENSE) file for details.
87
+
88
+ ---
89
+
90
+ ## ๐Ÿค Contributing
91
+ Contributions are welcome!
92
+ If you have an improvement or find a bug:
93
+ 1. Fork the repo
94
+ 2. Create your branch
95
+ 3. Submit a pull request
96
+
97
+ ---
98
+
99
+ ## ๐Ÿ’ก About
100
+ This project is part of a personal utility toolkit used to maintain a consistent, clean architecture across Python projects.
101
+ Originally authored by [Ariana](https://github.com/ariana126).
102
+ EOF
@@ -0,0 +1,88 @@
1
+ # underpy ๐Ÿ
2
+
3
+ **Reusable Python base classes for clean, maintainable, and scalable projects.**
4
+ Underpy provides foundational building blocks such as encapsulated data, immutable objects, service patterns, and JSON typing โ€” so you can start every new project with a solid, consistent architecture.
5
+
6
+ ---
7
+
8
+ ## โœจ Features
9
+
10
+ - **Encapsulated** โ€“ Hide internals and expose clean public APIs
11
+ - **Immutable** โ€“ Prevent changes to objects after initialization
12
+ - **Service Class (Singleton)** โ€“ Enforce a single point of access for core services
13
+ - **JSON Type** โ€“ Typed JSON data handling made easy
14
+ - **Tested with Pytest + AssertPy** โ€“ Reliable and expressive tests
15
+
16
+ ---
17
+
18
+ ## ๐Ÿ“ฆ Installation
19
+
20
+ Install from source (local development):
21
+
22
+ ```bash
23
+ pip install git+https://github.com/ariana126/underpy.git
24
+ ```
25
+
26
+ ---
27
+
28
+ ## ๐Ÿš€ Quick Start
29
+
30
+ ### Example: Encapsulated
31
+ ```python
32
+ from underpy import Encapsulated
33
+
34
+ class User(Encapsulated):
35
+ def __init__(self, username):
36
+ self._username = username
37
+
38
+ def get_username(self):
39
+ return self._username
40
+
41
+ user = User("ariana")
42
+ print(user.get_username()) # โœ… "ariana"
43
+ # Direct access is avoided: user._username
44
+ ```
45
+
46
+ ---
47
+
48
+ ### Example: Immutable
49
+ ```python
50
+ from underpy import Immutable
51
+
52
+ class Config(Immutable):
53
+ def __init__(self, host, port):
54
+ self.host = host
55
+ self.port = port
56
+
57
+ cfg = Config("localhost", 8080)
58
+ # cfg.port = 9000 # โŒ Raises AttributeError
59
+ ```
60
+
61
+ ---
62
+
63
+ ## ๐Ÿงช Running Tests
64
+
65
+ ```bash
66
+ pytest
67
+ ```
68
+
69
+ ---
70
+
71
+ ## ๐Ÿ“„ License
72
+ This project is licensed under the **MIT License** โ€“ see the [LICENSE](LICENSE) file for details.
73
+
74
+ ---
75
+
76
+ ## ๐Ÿค Contributing
77
+ Contributions are welcome!
78
+ If you have an improvement or find a bug:
79
+ 1. Fork the repo
80
+ 2. Create your branch
81
+ 3. Submit a pull request
82
+
83
+ ---
84
+
85
+ ## ๐Ÿ’ก About
86
+ This project is part of a personal utility toolkit used to maintain a consistent, clean architecture across Python projects.
87
+ Originally authored by [Ariana](https://github.com/ariana126).
88
+ EOF
@@ -0,0 +1,22 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel", "twine"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "underpyx"
7
+ version = "0.1.0"
8
+ description = "Fundamental utilities in python, such as encapsulation, immutability, service patterns, and JSON typing in Python."
9
+ authors = [
10
+ { name = "Ariana Maghsoudi", email = "ariana.maghsoudi82@gmail.com" }
11
+ ]
12
+ readme = "README.md"
13
+ license = { text = "MIT" }
14
+ requires-python = ">=3.8"
15
+ dependencies = []
16
+
17
+ [project.optional-dependencies]
18
+ dev = ["pytest", "assertpy"]
19
+
20
+ [tool.setuptools.packages.find]
21
+ include = ["underpy*"]
22
+ exclude = ["tests*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,6 @@
1
+ from .encapsulation import Encapsulated
2
+ from .mutability import Immutable
3
+ from .service import ServiceClass
4
+ from .typing import JSON
5
+
6
+ __all__ = ["Encapsulated", "Immutable", "ServiceClass", "JSON"]
@@ -0,0 +1,75 @@
1
+ import inspect
2
+ from abc import ABC
3
+
4
+ # TODO: Make _check_ functions and _initialized private.
5
+
6
+ class Encapsulated(ABC):
7
+ def __getattribute__(self, name):
8
+ # Skip internal attributes and methods to avoid recursion
9
+ if (name.startswith('_Encapsulated__') or
10
+ name.startswith('_check_') or
11
+ name in ('_initialized', '__dict__', '__class__')):
12
+ return object.__getattribute__(self, name)
13
+
14
+ # Handle encapsulation
15
+ if name.startswith('__') and not name.endswith('__'):
16
+ # Private attribute
17
+ if not self._check_private_access(name):
18
+ raise AttributeError(f"Cannot access private attribute {name}")
19
+ elif name.startswith('_') and not name.startswith('__'):
20
+ # Protected attribute
21
+ if not self._check_protected_access(name):
22
+ raise AttributeError(f"Cannot access protected attribute {name}")
23
+
24
+ return object.__getattribute__(self, name)
25
+
26
+ def __setattr__(self, name, value):
27
+ # Handle encapsulation checks first
28
+ if hasattr(self, '_initialized'): # Only check after initialization
29
+ if name.startswith('__') and not name.endswith('__'):
30
+ # Private attribute - check access
31
+ if not self._check_private_access(name):
32
+ raise AttributeError(f"Cannot access private attribute {name}")
33
+ elif name.startswith('_') and not name.startswith('__'):
34
+ # Protected attribute - check access
35
+ if not self._check_protected_access(name):
36
+ raise AttributeError(f"Cannot access protected attribute {name}")
37
+
38
+ # Call parent's __setattr__ (cooperative inheritance)
39
+ super().__setattr__(name, value)
40
+
41
+ def _check_private_access(self, name):
42
+ """Check if private attribute access is allowed"""
43
+ try:
44
+ frame = inspect.currentframe().f_back.f_back
45
+ caller_locals = frame.f_locals
46
+ caller_class = None
47
+
48
+ if 'self' in caller_locals:
49
+ caller_class = caller_locals['self'].__class__
50
+ elif 'cls' in caller_locals:
51
+ caller_class = caller_locals['cls']
52
+
53
+ # Private access only allowed from exact same class
54
+ return caller_class is self.__class__
55
+ except:
56
+ return False
57
+
58
+ def _check_protected_access(self, name):
59
+ """Check if protected attribute access is allowed"""
60
+ try:
61
+ frame = inspect.currentframe().f_back.f_back
62
+ caller_locals = frame.f_locals
63
+ caller_class = None
64
+
65
+ if 'self' in caller_locals:
66
+ caller_class = caller_locals['self'].__class__
67
+ elif 'cls' in caller_locals:
68
+ caller_class = caller_locals['cls']
69
+
70
+ # Protected access allowed from same class or subclasses
71
+ return (caller_class is self.__class__ or
72
+ (caller_class and issubclass(caller_class, self.__class__)) or
73
+ (caller_class and issubclass(self.__class__, caller_class)))
74
+ except:
75
+ return False
@@ -0,0 +1,20 @@
1
+ from abc import ABC, ABCMeta
2
+
3
+ # TODO: Make _initialized private.
4
+
5
+ class ImmutableMeta(ABCMeta):
6
+ def __call__(cls, *args, **kwargs):
7
+ instance = super().__call__(*args, **kwargs)
8
+ instance._initialized = True
9
+ return instance
10
+
11
+ class Immutable(ABC, metaclass=ImmutableMeta):
12
+ def __setattr__(self, name, value):
13
+ if hasattr(self, '_initialized'):
14
+ raise AttributeError(f"Cannot modify immutable object")
15
+ super().__setattr__(name, value)
16
+
17
+ def __delattr__(self, name):
18
+ if hasattr(self, '_initialized'):
19
+ raise AttributeError(f"Cannot delete from immutable object")
20
+ super().__delattr__(name)
@@ -0,0 +1,6 @@
1
+ from abc import ABC
2
+ from underpy import Encapsulated, Immutable
3
+
4
+
5
+ class ServiceClass(Encapsulated, Immutable, ABC):
6
+ pass
@@ -0,0 +1,11 @@
1
+ from typing import Union, List, Dict
2
+
3
+ JSON = Union[
4
+ str,
5
+ int,
6
+ float,
7
+ bool,
8
+ None,
9
+ Dict[str, "JSON"],
10
+ List["JSON"]
11
+ ]
@@ -0,0 +1,102 @@
1
+ Metadata-Version: 2.4
2
+ Name: underpyx
3
+ Version: 0.1.0
4
+ Summary: Fundamental utilities in python, such as encapsulation, immutability, service patterns, and JSON typing in Python.
5
+ Author-email: Ariana Maghsoudi <ariana.maghsoudi82@gmail.com>
6
+ License: MIT
7
+ Requires-Python: >=3.8
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Provides-Extra: dev
11
+ Requires-Dist: pytest; extra == "dev"
12
+ Requires-Dist: assertpy; extra == "dev"
13
+ Dynamic: license-file
14
+
15
+ # underpy ๐Ÿ
16
+
17
+ **Reusable Python base classes for clean, maintainable, and scalable projects.**
18
+ Underpy provides foundational building blocks such as encapsulated data, immutable objects, service patterns, and JSON typing โ€” so you can start every new project with a solid, consistent architecture.
19
+
20
+ ---
21
+
22
+ ## โœจ Features
23
+
24
+ - **Encapsulated** โ€“ Hide internals and expose clean public APIs
25
+ - **Immutable** โ€“ Prevent changes to objects after initialization
26
+ - **Service Class (Singleton)** โ€“ Enforce a single point of access for core services
27
+ - **JSON Type** โ€“ Typed JSON data handling made easy
28
+ - **Tested with Pytest + AssertPy** โ€“ Reliable and expressive tests
29
+
30
+ ---
31
+
32
+ ## ๐Ÿ“ฆ Installation
33
+
34
+ Install from source (local development):
35
+
36
+ ```bash
37
+ pip install git+https://github.com/ariana126/underpy.git
38
+ ```
39
+
40
+ ---
41
+
42
+ ## ๐Ÿš€ Quick Start
43
+
44
+ ### Example: Encapsulated
45
+ ```python
46
+ from underpy import Encapsulated
47
+
48
+ class User(Encapsulated):
49
+ def __init__(self, username):
50
+ self._username = username
51
+
52
+ def get_username(self):
53
+ return self._username
54
+
55
+ user = User("ariana")
56
+ print(user.get_username()) # โœ… "ariana"
57
+ # Direct access is avoided: user._username
58
+ ```
59
+
60
+ ---
61
+
62
+ ### Example: Immutable
63
+ ```python
64
+ from underpy import Immutable
65
+
66
+ class Config(Immutable):
67
+ def __init__(self, host, port):
68
+ self.host = host
69
+ self.port = port
70
+
71
+ cfg = Config("localhost", 8080)
72
+ # cfg.port = 9000 # โŒ Raises AttributeError
73
+ ```
74
+
75
+ ---
76
+
77
+ ## ๐Ÿงช Running Tests
78
+
79
+ ```bash
80
+ pytest
81
+ ```
82
+
83
+ ---
84
+
85
+ ## ๐Ÿ“„ License
86
+ This project is licensed under the **MIT License** โ€“ see the [LICENSE](LICENSE) file for details.
87
+
88
+ ---
89
+
90
+ ## ๐Ÿค Contributing
91
+ Contributions are welcome!
92
+ If you have an improvement or find a bug:
93
+ 1. Fork the repo
94
+ 2. Create your branch
95
+ 3. Submit a pull request
96
+
97
+ ---
98
+
99
+ ## ๐Ÿ’ก About
100
+ This project is part of a personal utility toolkit used to maintain a consistent, clean architecture across Python projects.
101
+ Originally authored by [Ariana](https://github.com/ariana126).
102
+ EOF
@@ -0,0 +1,14 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ underpy/__init__.py
6
+ underpy/encapsulation.py
7
+ underpy/mutability.py
8
+ underpy/service.py
9
+ underpy/typing.py
10
+ underpyx.egg-info/PKG-INFO
11
+ underpyx.egg-info/SOURCES.txt
12
+ underpyx.egg-info/dependency_links.txt
13
+ underpyx.egg-info/requires.txt
14
+ underpyx.egg-info/top_level.txt
@@ -0,0 +1,4 @@
1
+
2
+ [dev]
3
+ pytest
4
+ assertpy
@@ -0,0 +1 @@
1
+ underpy