Skip to content

Commit fe2b18b

Browse files
split off from py-pmp-manip; prepared first release
0 parents  commit fe2b18b

28 files changed

+6777
-0
lines changed

.github/workflows/publish.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Publish Python package
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*.*.*" # only run when you push a version tag
7+
8+
jobs:
9+
build-and-publish:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- uses: actions/checkout@v4
14+
15+
- name: Set up Python
16+
uses: actions/setup-python@v5
17+
with:
18+
python-version: "3.14"
19+
20+
- name: Install build tools
21+
run: |
22+
python -m pip install --upgrade pip
23+
pip install --upgrade build twine
24+
25+
- name: Build package
26+
run: python -m build
27+
28+
- name: Publish to PyPI
29+
run: twine upload dist/*
30+
env:
31+
TWINE_USERNAME: "__token__"
32+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
__pycache__/
2+
gen_ext_opcode_info/
3+
.coverage
4+
.pytest_cache/
5+
htmlcov/
6+
dist/
7+
*.egg-info/
8+
build/

.vscode/settings.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"python.testing.pytestArgs": [
3+
"tests"
4+
],
5+
"python.testing.unittestEnabled": false,
6+
"python.testing.pytestEnabled": true
7+
}

LICENSE

Lines changed: 674 additions & 0 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
<div align="center">
2+
3+
# gceutils
4+
5+
Python utilities for DRY tools, rich repr/validation helpers, object-tree iteration & more.
6+
7+
</div>
8+
9+
---
10+
11+
## Features
12+
13+
The public API is kept intentionally small. Highlights:
14+
15+
- `grepr_dataclass`, `field`: Enhanced dataclasses with validation and improved representation
16+
- `HasGreprValidate`: Protocol reflecting the effects of `grepr_dataclass`
17+
- `AbstractTreePath`: Path abstraction for nested object trees (attributes, indexes, keys)
18+
- `NotSet`, `NotSetType`: Unique sentinel useful for keyword arguments and defaults
19+
- `enforce_argument_types`: Runtime enforcement of function argument types from annotations
20+
- `enforce_type`: Recursive type checking against rich typing constructs
21+
- `DualKeyDict`: Dictionary supporting two linked key spaces with full mapping features
22+
- `GU_PathValidationError`: Validation error carrying an `AbstractTreePath` context
23+
24+
- `read_all_files_of_zip`: Read all files from a ZIP into a name→bytes map (with robust errors)
25+
- `read_file_text`: Read text files with encoding and solid error reporting
26+
- `write_file_text`: Write text files with encoding and solid error reporting
27+
- `delete_file`: Remove a file with clear, specific exceptions
28+
- `delete_directory`: Recursively remove a directory with clear, specific exceptions
29+
- `create_zip_file`: Build a ZIP file from an in-memory name→bytes mapping
30+
- `file_exists`: Lightweight existence check for a path
31+
32+
- `KeyReprDict`: Dict wrapper whose repr displays only keys
33+
- `grepr`: Flexible pretty-printer for dataclasses, collections, dicts, and `DualKeyDict`
34+
- `GEnum`: Enum base class with concise `Class.Member` repr
35+
36+
- `TreeVisitor`: Recursive traversal over dataclass-based object trees with type filtering
37+
38+
- `ValidateAttribute`: Prebuilt validators (type/range/length/formats)
39+
- `is_valid_js_data_uri`: Validate JS data URIs
40+
- `is_valid_directory_path`: Validate an existing or creatable, writable directory path
41+
- `is_valid_url`: Validate basic HTTP(S) URLs with a domain
42+
43+
For the full, always-up-to-date list, see [features.md](features.md).
44+
45+
---
46+
47+
## Install
48+
49+
Python 3.12+ is required.
50+
51+
- From PyPI (if published):
52+
53+
```bash
54+
pip install gceutils
55+
```
56+
57+
- From source (editable):
58+
59+
```bash
60+
git clone https://github.com/GermanCodeEngineer/gceutils.git
61+
cd gceutils
62+
pip install -e .
63+
```
64+
65+
---
66+
67+
## Quick Examples
68+
69+
Validate attributes using built-in validators:
70+
71+
```python
72+
from gceutils import grepr_dataclass, ValidateAttribute as VA, AbstractTreePath, HasGreprValidate
73+
from datetime import date
74+
75+
@grepr_dataclass()
76+
class Config(HasGreprValidate): # HasGreprValidate is optional, just helps type checkers
77+
color: str
78+
79+
def post_validate(self, path: AbstractTreePath) -> None:
80+
# Additional validation can be done here if needed
81+
if date.today().weekday() == 1:
82+
VA.VA_HEX_COLOR(self, path, "color", condition="on mondays")
83+
84+
cfg = Config(color="#FF0956e") # not valid
85+
cfg.validate(path=AbstractTreePath(())) # Ensures color is a str and hex color
86+
```
87+
Output(abbreviated):
88+
```
89+
gceutils.errors.GU_InvalidValueError: on tuesdays: color of a __main__.Config must be a valid hex color eg. '#FF0956'
90+
```
91+
92+
Traverse an object tree and collect specific node types:
93+
94+
```python
95+
from gceutils import grepr_dataclass, grepr, TreeVisitor
96+
97+
@grepr_dataclass()
98+
class Node:
99+
name: str
100+
children: list["Node"]
101+
102+
root = Node("root", [Node("a", []), Node("b", [])])
103+
visitor = TreeVisitor.create_new_include_only([Node])
104+
matches = visitor.visit_tree(root) # {AbstractTreePath: Node}
105+
print("My matches:", grepr(matches))
106+
```
107+
Output:
108+
```
109+
My matches: {
110+
AbstractTreePath(.children[0]): Node(name="a", children=[]),
111+
AbstractTreePath(.children[1]): Node(name="b", children=[]),
112+
}
113+
```
114+
115+
Read all files from a ZIP and pretty-print a structure:
116+
117+
```python
118+
from gceutils import read_all_files_of_zip, grepr
119+
120+
contents = read_all_files_of_zip("archive.zip") # {"path/inside.txt": b"..."}
121+
print(grepr(list(contents.keys()), indent=2))
122+
```
123+
124+
---
125+
126+
## Testing
127+
128+
This repo uses `pytest`.
129+
130+
```bash
131+
pytest
132+
```
133+
134+
---
135+
136+
## Contributing
137+
138+
Contribution is welcomed and encouraged.
139+
140+
---
141+
142+
## License
143+
144+
GPL-3.0-or-later — see [LICENSE](LICENSE).
145+

TODO.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
TODOs Sorted by Priorites
2+
3+
Currently Working On: /
4+
5+
Changelog since last Release: /
6+
7+
High: /
8+
9+
Medium:
10+
- S: fix grepr multiline string issue
11+
=> also fix parent and subclass field order issue
12+
- S: investigate custom type object variables(example: vector extension)
13+
14+
Low:
15+
- M: replace remaining io actions with wrappers (eg. file_exists instead of os.path.exists)
16+
- S: move from os.path to pathlib
17+
18+
Formatting: /

conftest.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import sys
2+
from pathlib import Path
3+
4+
# Add src directory to Python path for tests
5+
src_path = Path(__file__).parent / "src"
6+
sys.path.insert(0, str(src_path))

docs/release_tutorial.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Preparing a New Release
2+
3+
### **1. Core Functionality**
4+
5+
* ✅ All planned features for the initial release should be implemented.
6+
* ✅ Features should work as expected; no critical bugs that break core functionality.
7+
* ✅ Edge cases and error handling should be covered.
8+
9+
---
10+
11+
### **2. Code Quality**
12+
13+
* ✅ Code is clean, readable, and follows PEP 8 style conventions.
14+
* ✅ Functions and classes are well-organized; modular design.
15+
* ✅ Unused code, debug prints, and commented-out sections are removed.
16+
* ✅ Temporary files and file calls are removed.
17+
* ✅ Proper exception handling is in place.
18+
19+
---
20+
21+
### **3. Testing**
22+
23+
* ✅ Unit tests cover critical functionality.
24+
* ✅ Optional: Integration tests to ensure components work together.
25+
* ✅ All tests pass consistently.
26+
27+
---
28+
29+
### **4. Stability & Security**
30+
31+
* ✅ No hard-coded secrets (API keys, passwords) in the repo.
32+
* ✅ Avoid known security vulnerabilities in dependencies.
33+
* ✅ Graceful shutdown and resource cleanup (files, network connections).
34+
35+
---
36+
37+
# Next Steps
38+
1. **Check for dependency updates** to see if newer versions are available:
39+
```bash
40+
python -m scripts.check_dependency_updates
41+
```
42+
2. **Ensure all tests are successful** and generate coverage report:
43+
```bash
44+
coverage run -m pytest tests/
45+
```
46+
```bash
47+
coverage html
48+
```
49+
3. View coverage report: **Are all critical code files covered?**
50+
4. **Review pyproject.toml** with the new dependencies:
51+
```bash
52+
python -m scripts.review_pyproject_toml
53+
```
54+
5. **Follow suggested changes**(except coverage and pipreqs) **and increase version number**
55+
6. **Build package locally**:
56+
```bash
57+
pip install --upgrade build
58+
python -m build
59+
```
60+
7. **Commit and Push all changes** (Just an Example):
61+
```bash
62+
git add -A
63+
git commit -m "prepare next release"
64+
git push
65+
```
66+
8. **Create a Git tag with the version number and Push** the tag to your remote:
67+
```bash
68+
git tag v1.2.3
69+
git push origin v1.2.3
70+
```
71+
9. **Verify CI passes on the release tag** (GitHub Actions)

pyproject.toml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
[project]
2+
name = "gceutils"
3+
version = "1.0.0"
4+
description = "Python utilities for DRY tools, rich repr/validation helpers, object-tree iteration & more."
5+
readme = { file = "README.md", content-type = "text/markdown" }
6+
requires-python = ">=3.12"
7+
license = { text = "GPL-3.0-or-later" }
8+
authors = [{name = "GermanCodeEngineer", email = "germancodeengineer@proton.me"}]
9+
10+
# sometimes auto generated
11+
dependencies = []
12+
13+
classifiers = [
14+
"Development Status :: 5 - Production/Stable",
15+
"Programming Language :: Python :: 3.12",
16+
"Programming Language :: Python :: 3.13",
17+
"Programming Language :: Python :: 3.14",
18+
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
19+
"Operating System :: OS Independent",
20+
]
21+
22+
[project.urls]
23+
Homepage = "https://github.com/GermanCodeEngineer/gceutils/"
24+
Repository = "https://github.com/GermanCodeEngineer/gceutils/"
25+
Issues = "https://github.com/GermanCodeEngineer/gceutils/issues/"
26+
27+
# sometimes auto generated
28+
[project.optional-dependencies]
29+
dev = [
30+
"Requests~=2.32",
31+
"pytest~=9.0",
32+
"tomlkit~=0.14",
33+
"coverage~=7.13",
34+
"pipreqs~=0.4",
35+
]
36+
37+
[build-system]
38+
requires = ["setuptools>=68", "wheel"]
39+
build-backend = "setuptools.build_meta"
40+
41+
[tool.setuptools.packages.find]
42+
where = ["src"]
43+
include = ["gceutils*"]
44+
45+
[tool.setuptools]
46+
include-package-data = true

0 commit comments

Comments
 (0)