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 +21 -0
- underpyx-0.1.0/MANIFEST.in +3 -0
- underpyx-0.1.0/PKG-INFO +102 -0
- underpyx-0.1.0/README.md +88 -0
- underpyx-0.1.0/pyproject.toml +22 -0
- underpyx-0.1.0/setup.cfg +4 -0
- underpyx-0.1.0/underpy/__init__.py +6 -0
- underpyx-0.1.0/underpy/encapsulation.py +75 -0
- underpyx-0.1.0/underpy/mutability.py +20 -0
- underpyx-0.1.0/underpy/service.py +6 -0
- underpyx-0.1.0/underpy/typing.py +11 -0
- underpyx-0.1.0/underpyx.egg-info/PKG-INFO +102 -0
- underpyx-0.1.0/underpyx.egg-info/SOURCES.txt +14 -0
- underpyx-0.1.0/underpyx.egg-info/dependency_links.txt +1 -0
- underpyx-0.1.0/underpyx.egg-info/requires.txt +4 -0
- underpyx-0.1.0/underpyx.egg-info/top_level.txt +1 -0
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.
|
underpyx-0.1.0/PKG-INFO
ADDED
|
@@ -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
|
underpyx-0.1.0/README.md
ADDED
|
@@ -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*"]
|
underpyx-0.1.0/setup.cfg
ADDED
|
@@ -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,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 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
underpy
|