artinforge 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.
@@ -0,0 +1,6 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+ - First draft of ArtinForge.
5
+ - Text, file, JSON, HTTP, local server, system information and CLI helpers.
6
+ - No mandatory third-party runtime dependencies.
@@ -0,0 +1,17 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Artin Karimian
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.
@@ -0,0 +1,5 @@
1
+ include README.md
2
+ include LICENSE
3
+ include CHANGELOG.md
4
+ include SECURITY.md
5
+ recursive-include src/artinforge/assets *
@@ -0,0 +1,96 @@
1
+ Metadata-Version: 2.4
2
+ Name: artinforge
3
+ Version: 0.1.0
4
+ Summary: A lightweight, modular Python toolkit for developer utilities.
5
+ Author: Artin Karimian
6
+ License-Expression: MIT
7
+ Keywords: python,developer-tools,automation,files,json,networking
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3 :: Only
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.9
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Dynamic: license-file
15
+
16
+ # ArtinForge
17
+
18
+ **A lightweight, modular Python toolkit for everyday developer tasks.**
19
+
20
+ ArtinForge starts with useful, real features and avoids mandatory third-party runtime dependencies.
21
+
22
+ ## Features
23
+
24
+ - **Text utilities:** normalize whitespace, count words, truncate text, create slugs.
25
+ - **File utilities:** UTF-8 read/write, directory creation, file listing and size.
26
+ - **JSON storage:** read and save JSON, including Persian/Unicode text.
27
+ - **HTTP helpers:** GET text/JSON and POST JSON using Python's standard library.
28
+ - **Local development server:** serve a folder for local testing.
29
+ - **System information:** Python version, platform, executable and current directory.
30
+ - **Command line:** inspect runtime details and launch the local server.
31
+
32
+ > ArtinForge is a developer toolkit, not a bundled AI model. AI features require a model or service to be connected separately.
33
+
34
+ ## Install
35
+
36
+ ```bash
37
+ python -m pip install artinforge
38
+ ```
39
+
40
+ If a package mirror is stale, check the official PyPI index:
41
+
42
+ ```bash
43
+ python -m pip install --index-url https://pypi.org/simple artinforge
44
+ ```
45
+
46
+ The name must be available on PyPI before the first release. Check the project page before publishing.
47
+
48
+ ## Quick example
49
+
50
+ ```python
51
+ from artinforge import text, files, jsonstore
52
+
53
+ print(text.slugify("Hello ArtinForge!"))
54
+ files.write_text("demo/hello.txt", "سلام آرتین")
55
+ jsonstore.save("demo/settings.json", {"language": "fa", "theme": "dark"})
56
+ print(jsonstore.load("demo/settings.json", default={}))
57
+ ```
58
+
59
+ ## Start a local test server
60
+
61
+ ```bash
62
+ artinforge serve --host 127.0.0.1 --port 8765 --directory .
63
+ ```
64
+
65
+ Then open `http://127.0.0.1:8765` in your browser. This is for local development; do not expose it to the public internet without a security review and appropriate protections.
66
+
67
+ ## Commands
68
+
69
+ ```bash
70
+ artinforge --help
71
+ artinforge info
72
+ artinforge serve --host 127.0.0.1 --port 8765 --directory .
73
+ ```
74
+
75
+ ## Package the project
76
+
77
+ From the folder containing `pyproject.toml`:
78
+
79
+ ```bash
80
+ python -m pip install --upgrade build twine
81
+ python -m build
82
+ python -m twine check dist/*
83
+ ```
84
+
85
+ Only after checks pass, upload with your PyPI token:
86
+
87
+ ```bash
88
+ python -m twine upload dist/*
89
+ ```
90
+
91
+ **Release rule:** every upload to PyPI needs a version that has not been published before. If you need to fix a release, increment `version` in `pyproject.toml`, rebuild from a clean `dist/` directory, and upload the new version. A mirror may take time to sync, so verify against the official PyPI index.
92
+
93
+
94
+ ## License
95
+
96
+ MIT. See `LICENSE`.
@@ -0,0 +1,81 @@
1
+ # ArtinForge
2
+
3
+ **A lightweight, modular Python toolkit for everyday developer tasks.**
4
+
5
+ ArtinForge starts with useful, real features and avoids mandatory third-party runtime dependencies.
6
+
7
+ ## Features
8
+
9
+ - **Text utilities:** normalize whitespace, count words, truncate text, create slugs.
10
+ - **File utilities:** UTF-8 read/write, directory creation, file listing and size.
11
+ - **JSON storage:** read and save JSON, including Persian/Unicode text.
12
+ - **HTTP helpers:** GET text/JSON and POST JSON using Python's standard library.
13
+ - **Local development server:** serve a folder for local testing.
14
+ - **System information:** Python version, platform, executable and current directory.
15
+ - **Command line:** inspect runtime details and launch the local server.
16
+
17
+ > ArtinForge is a developer toolkit, not a bundled AI model. AI features require a model or service to be connected separately.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ python -m pip install artinforge
23
+ ```
24
+
25
+ If a package mirror is stale, check the official PyPI index:
26
+
27
+ ```bash
28
+ python -m pip install --index-url https://pypi.org/simple artinforge
29
+ ```
30
+
31
+ The name must be available on PyPI before the first release. Check the project page before publishing.
32
+
33
+ ## Quick example
34
+
35
+ ```python
36
+ from artinforge import text, files, jsonstore
37
+
38
+ print(text.slugify("Hello ArtinForge!"))
39
+ files.write_text("demo/hello.txt", "سلام آرتین")
40
+ jsonstore.save("demo/settings.json", {"language": "fa", "theme": "dark"})
41
+ print(jsonstore.load("demo/settings.json", default={}))
42
+ ```
43
+
44
+ ## Start a local test server
45
+
46
+ ```bash
47
+ artinforge serve --host 127.0.0.1 --port 8765 --directory .
48
+ ```
49
+
50
+ Then open `http://127.0.0.1:8765` in your browser. This is for local development; do not expose it to the public internet without a security review and appropriate protections.
51
+
52
+ ## Commands
53
+
54
+ ```bash
55
+ artinforge --help
56
+ artinforge info
57
+ artinforge serve --host 127.0.0.1 --port 8765 --directory .
58
+ ```
59
+
60
+ ## Package the project
61
+
62
+ From the folder containing `pyproject.toml`:
63
+
64
+ ```bash
65
+ python -m pip install --upgrade build twine
66
+ python -m build
67
+ python -m twine check dist/*
68
+ ```
69
+
70
+ Only after checks pass, upload with your PyPI token:
71
+
72
+ ```bash
73
+ python -m twine upload dist/*
74
+ ```
75
+
76
+ **Release rule:** every upload to PyPI needs a version that has not been published before. If you need to fix a release, increment `version` in `pyproject.toml`, rebuild from a clean `dist/` directory, and upload the new version. A mirror may take time to sync, so verify against the official PyPI index.
77
+
78
+
79
+ ## License
80
+
81
+ MIT. See `LICENSE`.
@@ -0,0 +1,3 @@
1
+ # Security
2
+
3
+ The development server is intended for local development and trusted networks. Do not expose it publicly without authentication, TLS, request limits, logging, and a security review. Never share PyPI API tokens or put them into source code.
@@ -0,0 +1,28 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "artinforge"
7
+ version = "0.1.0"
8
+ description = "A lightweight, modular Python toolkit for developer utilities."
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = "MIT"
12
+ authors = [{name = "Artin Karimian"}]
13
+ keywords = ["python", "developer-tools", "automation", "files", "json", "networking"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3 :: Only",
17
+ "Operating System :: OS Independent",
18
+ ]
19
+ dependencies = []
20
+
21
+ [project.scripts]
22
+ artinforge = "artinforge.cli:main"
23
+
24
+ [tool.setuptools.packages.find]
25
+ where = ["src"]
26
+
27
+ [tool.setuptools.package-data]
28
+ artinforge = ["assets/*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,4 @@
1
+ """ArtinForge lightweight developer toolkit."""
2
+ __version__ = "0.1.0"
3
+ from . import files, jsonstore, net, system, text
4
+ __all__ = ["files", "jsonstore", "net", "system", "text", "__version__"]
@@ -0,0 +1,24 @@
1
+ """ArtinForge command line interface."""
2
+ import argparse
3
+ import json
4
+ from .server import LocalServer
5
+ from .system import info
6
+
7
+ def main():
8
+ parser = argparse.ArgumentParser(prog="artinforge", description="ArtinForge developer toolkit")
9
+ sub = parser.add_subparsers(dest="command")
10
+ sub.add_parser("info", help="show Python/platform information")
11
+ serve = sub.add_parser("serve", help="serve a directory for local development")
12
+ serve.add_argument("--host", default="127.0.0.1")
13
+ serve.add_argument("--port", type=int, default=8765)
14
+ serve.add_argument("--directory", default=".")
15
+ args = parser.parse_args()
16
+ if args.command == "info":
17
+ print(json.dumps(info(), indent=2, ensure_ascii=False))
18
+ elif args.command == "serve":
19
+ LocalServer(args.host, args.port, args.directory).serve()
20
+ else:
21
+ parser.print_help()
22
+
23
+ if __name__ == "__main__":
24
+ main()
@@ -0,0 +1,22 @@
1
+ """Filesystem helpers."""
2
+ from pathlib import Path
3
+
4
+ def ensure_dir(path) -> Path:
5
+ target = Path(path)
6
+ target.mkdir(parents=True, exist_ok=True)
7
+ return target
8
+
9
+ def read_text(path, encoding="utf-8") -> str:
10
+ return Path(path).read_text(encoding=encoding)
11
+
12
+ def write_text(path, content, encoding="utf-8") -> Path:
13
+ target = Path(path)
14
+ target.parent.mkdir(parents=True, exist_ok=True)
15
+ target.write_text(str(content), encoding=encoding)
16
+ return target
17
+
18
+ def file_size(path) -> int:
19
+ return Path(path).stat().st_size
20
+
21
+ def list_files(path=".", pattern="*") -> list:
22
+ return sorted(p for p in Path(path).glob(pattern) if p.is_file())
@@ -0,0 +1,23 @@
1
+ """JSON read/write helpers."""
2
+ import json
3
+ from pathlib import Path
4
+
5
+ def load(path, default=None, encoding="utf-8"):
6
+ target = Path(path)
7
+ if not target.exists():
8
+ return default
9
+ try:
10
+ with target.open("r", encoding=encoding) as stream:
11
+ return json.load(stream)
12
+ except (json.JSONDecodeError, OSError) as exc:
13
+ raise ValueError(f"Could not load JSON from {target}: {exc}") from exc
14
+
15
+ def save(path, data, *, indent=2, ensure_ascii=False, encoding="utf-8"):
16
+ target = Path(path)
17
+ target.parent.mkdir(parents=True, exist_ok=True)
18
+ temp = target.with_suffix(target.suffix + ".tmp")
19
+ with temp.open("w", encoding=encoding) as stream:
20
+ json.dump(data, stream, indent=indent, ensure_ascii=ensure_ascii)
21
+ stream.write("\n")
22
+ temp.replace(target)
23
+ return target
@@ -0,0 +1,37 @@
1
+ """Small HTTP helpers using Python's standard library."""
2
+ import json
3
+ from urllib.error import HTTPError, URLError
4
+ from urllib.parse import urlparse
5
+ from urllib.request import Request, urlopen
6
+
7
+ def validate_url(url):
8
+ parsed = urlparse(url)
9
+ if parsed.scheme not in ("http", "https") or not parsed.netloc:
10
+ raise ValueError("Expected an absolute http:// or https:// URL")
11
+ return url
12
+
13
+ def get(url, *, timeout=10, headers=None):
14
+ request = Request(validate_url(url), headers=headers or {}, method="GET")
15
+ try:
16
+ with urlopen(request, timeout=timeout) as response:
17
+ return response.read()
18
+ except (HTTPError, URLError, TimeoutError) as exc:
19
+ raise RuntimeError(f"GET request failed: {exc}") from exc
20
+
21
+ def get_text(url, *, timeout=10, encoding="utf-8", headers=None):
22
+ return get(url, timeout=timeout, headers=headers).decode(encoding, errors="replace")
23
+
24
+ def get_json(url, *, timeout=10, headers=None):
25
+ return json.loads(get_text(url, timeout=timeout, headers=headers))
26
+
27
+ def post_json(url, data, *, timeout=10, headers=None):
28
+ request_headers = {"Content-Type": "application/json"}
29
+ request_headers.update(headers or {})
30
+ request = Request(validate_url(url), data=json.dumps(data).encode("utf-8"),
31
+ headers=request_headers, method="POST")
32
+ try:
33
+ with urlopen(request, timeout=timeout) as response:
34
+ body = response.read().decode("utf-8", errors="replace")
35
+ return json.loads(body) if body else None
36
+ except (HTTPError, URLError, TimeoutError) as exc:
37
+ raise RuntimeError(f"POST request failed: {exc}") from exc
@@ -0,0 +1,27 @@
1
+ """Local static-file server for development."""
2
+ from functools import partial
3
+ from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
4
+ from pathlib import Path
5
+
6
+ class LocalServer:
7
+ def __init__(self, host="127.0.0.1", port=8765, directory="."):
8
+ if not 0 <= int(port) <= 65535:
9
+ raise ValueError("port must be between 0 and 65535")
10
+ self.host, self.port = host, int(port)
11
+ self.directory = str(Path(directory).resolve())
12
+ self._server = ThreadingHTTPServer(
13
+ (self.host, self.port),
14
+ partial(SimpleHTTPRequestHandler, directory=self.directory)
15
+ )
16
+
17
+ def serve(self):
18
+ print("ARTINFORGE LOCAL SERVER")
19
+ print(f"Serving: {self.directory}")
20
+ print(f"Open: http://{self.host}:{self._server.server_address[1]}")
21
+ print("Press Ctrl+C to stop.")
22
+ try:
23
+ self._server.serve_forever()
24
+ except KeyboardInterrupt:
25
+ print("\nStopping server...")
26
+ finally:
27
+ self._server.server_close()
@@ -0,0 +1,12 @@
1
+ """Basic runtime information."""
2
+ import os
3
+ import platform
4
+ import sys
5
+
6
+ def info() -> dict:
7
+ return {"python": sys.version.split()[0],
8
+ "implementation": platform.python_implementation(),
9
+ "platform": platform.platform(),
10
+ "executable": sys.executable,
11
+ "current_directory": os.getcwd(),
12
+ "pid": os.getpid()}
@@ -0,0 +1,23 @@
1
+ """Unicode-friendly text utilities."""
2
+ import re
3
+ import unicodedata
4
+
5
+ def normalize_whitespace(value: str) -> str:
6
+ return " ".join(str(value).split())
7
+
8
+ def word_count(value: str) -> int:
9
+ return len(str(value).split())
10
+
11
+ def truncate(value: str, length: int, suffix: str = "...") -> str:
12
+ if length < 0:
13
+ raise ValueError("length must be >= 0")
14
+ value = str(value)
15
+ if len(value) <= length:
16
+ return value
17
+ if length <= len(suffix):
18
+ return suffix[:length]
19
+ return value[:length-len(suffix)] + suffix
20
+
21
+ def slugify(value: str, separator: str = "-") -> str:
22
+ value = unicodedata.normalize("NFKD", str(value)).encode("ascii", "ignore").decode()
23
+ return re.sub(r"[^a-zA-Z0-9]+", separator, value.lower()).strip(separator)
@@ -0,0 +1,96 @@
1
+ Metadata-Version: 2.4
2
+ Name: artinforge
3
+ Version: 0.1.0
4
+ Summary: A lightweight, modular Python toolkit for developer utilities.
5
+ Author: Artin Karimian
6
+ License-Expression: MIT
7
+ Keywords: python,developer-tools,automation,files,json,networking
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3 :: Only
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.9
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Dynamic: license-file
15
+
16
+ # ArtinForge
17
+
18
+ **A lightweight, modular Python toolkit for everyday developer tasks.**
19
+
20
+ ArtinForge starts with useful, real features and avoids mandatory third-party runtime dependencies.
21
+
22
+ ## Features
23
+
24
+ - **Text utilities:** normalize whitespace, count words, truncate text, create slugs.
25
+ - **File utilities:** UTF-8 read/write, directory creation, file listing and size.
26
+ - **JSON storage:** read and save JSON, including Persian/Unicode text.
27
+ - **HTTP helpers:** GET text/JSON and POST JSON using Python's standard library.
28
+ - **Local development server:** serve a folder for local testing.
29
+ - **System information:** Python version, platform, executable and current directory.
30
+ - **Command line:** inspect runtime details and launch the local server.
31
+
32
+ > ArtinForge is a developer toolkit, not a bundled AI model. AI features require a model or service to be connected separately.
33
+
34
+ ## Install
35
+
36
+ ```bash
37
+ python -m pip install artinforge
38
+ ```
39
+
40
+ If a package mirror is stale, check the official PyPI index:
41
+
42
+ ```bash
43
+ python -m pip install --index-url https://pypi.org/simple artinforge
44
+ ```
45
+
46
+ The name must be available on PyPI before the first release. Check the project page before publishing.
47
+
48
+ ## Quick example
49
+
50
+ ```python
51
+ from artinforge import text, files, jsonstore
52
+
53
+ print(text.slugify("Hello ArtinForge!"))
54
+ files.write_text("demo/hello.txt", "سلام آرتین")
55
+ jsonstore.save("demo/settings.json", {"language": "fa", "theme": "dark"})
56
+ print(jsonstore.load("demo/settings.json", default={}))
57
+ ```
58
+
59
+ ## Start a local test server
60
+
61
+ ```bash
62
+ artinforge serve --host 127.0.0.1 --port 8765 --directory .
63
+ ```
64
+
65
+ Then open `http://127.0.0.1:8765` in your browser. This is for local development; do not expose it to the public internet without a security review and appropriate protections.
66
+
67
+ ## Commands
68
+
69
+ ```bash
70
+ artinforge --help
71
+ artinforge info
72
+ artinforge serve --host 127.0.0.1 --port 8765 --directory .
73
+ ```
74
+
75
+ ## Package the project
76
+
77
+ From the folder containing `pyproject.toml`:
78
+
79
+ ```bash
80
+ python -m pip install --upgrade build twine
81
+ python -m build
82
+ python -m twine check dist/*
83
+ ```
84
+
85
+ Only after checks pass, upload with your PyPI token:
86
+
87
+ ```bash
88
+ python -m twine upload dist/*
89
+ ```
90
+
91
+ **Release rule:** every upload to PyPI needs a version that has not been published before. If you need to fix a release, increment `version` in `pyproject.toml`, rebuild from a clean `dist/` directory, and upload the new version. A mirror may take time to sync, so verify against the official PyPI index.
92
+
93
+
94
+ ## License
95
+
96
+ MIT. See `LICENSE`.
@@ -0,0 +1,21 @@
1
+ CHANGELOG.md
2
+ LICENSE
3
+ MANIFEST.in
4
+ README.md
5
+ SECURITY.md
6
+ pyproject.toml
7
+ src/artinforge/__init__.py
8
+ src/artinforge/cli.py
9
+ src/artinforge/files.py
10
+ src/artinforge/jsonstore.py
11
+ src/artinforge/net.py
12
+ src/artinforge/server.py
13
+ src/artinforge/system.py
14
+ src/artinforge/text.py
15
+ src/artinforge.egg-info/PKG-INFO
16
+ src/artinforge.egg-info/SOURCES.txt
17
+ src/artinforge.egg-info/dependency_links.txt
18
+ src/artinforge.egg-info/entry_points.txt
19
+ src/artinforge.egg-info/top_level.txt
20
+ src/artinforge/assets/artinforge-banner.png
21
+ tests/test_core.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ artinforge = artinforge.cli:main
@@ -0,0 +1 @@
1
+ artinforge
@@ -0,0 +1,22 @@
1
+ from artinforge import files, jsonstore, text, system
2
+
3
+ def test_text():
4
+ assert text.word_count("one two") == 2
5
+ assert text.normalize_whitespace(" a\n b ") == "a b"
6
+ assert text.truncate("abcdef", 4) == "a..."
7
+ assert text.slugify("Hello World!") == "hello-world"
8
+
9
+ def test_files(tmp_path):
10
+ p = files.write_text(tmp_path / "sub" / "hi.txt", "سلام")
11
+ assert files.read_text(p) == "سلام"
12
+ assert files.file_size(p) > 0
13
+ assert len(files.list_files(tmp_path / "sub")) == 1
14
+
15
+ def test_json(tmp_path):
16
+ p = tmp_path / "sub" / "x.json"
17
+ jsonstore.save(p, {"message": "سلام"})
18
+ assert jsonstore.load(p) == {"message": "سلام"}
19
+ assert jsonstore.load(tmp_path / "missing.json", default={}) == {}
20
+
21
+ def test_system():
22
+ assert "python" in system.info()