artinforge 0.1.0__py3-none-any.whl

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.
artinforge/__init__.py ADDED
@@ -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__"]
Binary file
artinforge/cli.py ADDED
@@ -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()
artinforge/files.py ADDED
@@ -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
artinforge/net.py ADDED
@@ -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
artinforge/server.py ADDED
@@ -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()
artinforge/system.py ADDED
@@ -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()}
artinforge/text.py ADDED
@@ -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,15 @@
1
+ artinforge/__init__.py,sha256=obt0lVZLWKkTj0clJkh9AIcL_KlCq17MkwHeBhPLQq4,193
2
+ artinforge/cli.py,sha256=Fy18P2HVLk9cytID7D_6tdFwjIqyQnThQ8YAKRG6CPY,904
3
+ artinforge/files.py,sha256=kEn-96x-N9sP3o7DYTUT7McnpnCIbBK-Gg25twciQKA,660
4
+ artinforge/jsonstore.py,sha256=LxNTA5egPTDxMYn5QSrKNr4-jI-XIGvmL_G4xSOph94,831
5
+ artinforge/net.py,sha256=9U1lmljCJaNyfAvYG-_FzUeSn2nVfJ3bruJQSZmNGdE,1636
6
+ artinforge/server.py,sha256=DgZu80j2K5U9u3l3fy4V1A2cQrrjdWxoY1yOc3Hwpag,1040
7
+ artinforge/system.py,sha256=lW2URGbJX-S-9owF9Kzw-hCKiS8_Cfbn889vmNbb1BE,366
8
+ artinforge/text.py,sha256=9BLy4QONMy9d0b_X97XxCeHLtv4l8vnIh8RGCCDQdDo,764
9
+ artinforge/assets/artinforge-banner.png,sha256=9ThHZ9QVh6GixUZmzluU35FfcR8aKK8XMwBIuL-6CMU,1991563
10
+ artinforge-0.1.0.dist-info/licenses/LICENSE,sha256=qTWUb-x1Rt-H5lDFtGSiTzxcKgVaUro8MxTyhMuTph0,812
11
+ artinforge-0.1.0.dist-info/METADATA,sha256=QiJ95OqKCFdTXHbtgH4JbNzBQR9uYH98wwk-P-ZKsC0,3096
12
+ artinforge-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
13
+ artinforge-0.1.0.dist-info/entry_points.txt,sha256=-PFKVE-z34ej8hzKsHx5tQSiiovuJaVWdjl8v9NSrxE,51
14
+ artinforge-0.1.0.dist-info/top_level.txt,sha256=iprDOQxUc35LeDbVx3-a0nXXEfOvFDVAgRseBveUTZs,11
15
+ artinforge-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ artinforge = artinforge.cli:main
@@ -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 @@
1
+ artinforge