Nexom 0.1.3__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.
Files changed (39) hide show
  1. nexom/__init__.py +19 -0
  2. nexom/__main__.py +61 -0
  3. nexom/assets/error_page/error.html +44 -0
  4. nexom/assets/server/config.py +27 -0
  5. nexom/assets/server/gunicorn.conf.py +16 -0
  6. nexom/assets/server/pages/__init__.py +3 -0
  7. nexom/assets/server/pages/__pycache__/__init__.cpython-313.pyc +0 -0
  8. nexom/assets/server/pages/_templates.py +11 -0
  9. nexom/assets/server/pages/default.py +10 -0
  10. nexom/assets/server/pages/document.py +10 -0
  11. nexom/assets/server/router.py +18 -0
  12. nexom/assets/server/static/dog.jpeg +0 -0
  13. nexom/assets/server/static/style.css +39 -0
  14. nexom/assets/server/templates/base.html +18 -0
  15. nexom/assets/server/templates/default.html +7 -0
  16. nexom/assets/server/templates/document.html +169 -0
  17. nexom/assets/server/templates/footer.html +3 -0
  18. nexom/assets/server/templates/header.html +3 -0
  19. nexom/assets/server/wsgi.py +30 -0
  20. nexom/buildTools/__init__.py +0 -0
  21. nexom/buildTools/build.py +99 -0
  22. nexom/core/__init__.py +1 -0
  23. nexom/core/error.py +149 -0
  24. nexom/engine/__init__.py +1 -0
  25. nexom/engine/object_html_render.py +224 -0
  26. nexom/web/__init__.py +5 -0
  27. nexom/web/cookie.py +73 -0
  28. nexom/web/http_status_codes.py +72 -0
  29. nexom/web/middleware.py +51 -0
  30. nexom/web/path.py +125 -0
  31. nexom/web/request.py +62 -0
  32. nexom/web/response.py +146 -0
  33. nexom/web/template.py +115 -0
  34. nexom-0.1.3.dist-info/METADATA +168 -0
  35. nexom-0.1.3.dist-info/RECORD +39 -0
  36. nexom-0.1.3.dist-info/WHEEL +5 -0
  37. nexom-0.1.3.dist-info/entry_points.txt +2 -0
  38. nexom-0.1.3.dist-info/licenses/LICENSE +21 -0
  39. nexom-0.1.3.dist-info/top_level.txt +1 -0
nexom/__init__.py ADDED
@@ -0,0 +1,19 @@
1
+ """
2
+ NEXOM - A lightweight Python web framework.
3
+
4
+ NEXOM provides a simple and flexible foundation for building
5
+ WSGI-based web applications with minimal overhead.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from nexom.web.request import Request
11
+ from nexom.web.response import Response
12
+
13
+ __all__ = [
14
+ "Request",
15
+ "Response",
16
+ "__version__",
17
+ ]
18
+
19
+ __version__ = "0.1.2"
nexom/__main__.py ADDED
@@ -0,0 +1,61 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ from nexom.buildTools.build import server as build_server
8
+ from nexom.buildTools.build import ServerBuildOptions
9
+
10
+
11
+ def main(argv: list[str] | None = None) -> None:
12
+ parser = argparse.ArgumentParser(
13
+ prog="nexom",
14
+ description="Nexom Web Framework CLI",
15
+ )
16
+ subparsers = parser.add_subparsers(dest="command", required=True)
17
+
18
+ # test
19
+ subparsers.add_parser("test", help="Test Nexom installation")
20
+
21
+ # build-server
22
+ p = subparsers.add_parser(
23
+ "build-server",
24
+ help="Create a Nexom server project",
25
+ )
26
+ p.add_argument("server_name", help="Server project name")
27
+ p.add_argument(
28
+ "--out",
29
+ default=".",
30
+ help="Output directory (default: current directory)",
31
+ )
32
+ p.add_argument("--address", default="0.0.0.0", help="Bind address (default: 0.0.0.0)")
33
+ p.add_argument("--port", type=int, default=8080, help="Bind port (default: 8080)")
34
+ p.add_argument("--workers", type=int, default=4, help="Gunicorn workers (default: 4)")
35
+ p.add_argument("--reload", action="store_true", help="Enable auto-reload (development)")
36
+
37
+ args = parser.parse_args(argv)
38
+
39
+ if args.command == "test":
40
+ print("Hello Nexom Web Framework!")
41
+ return
42
+
43
+ if args.command == "build-server":
44
+ options = ServerBuildOptions(
45
+ address=args.address,
46
+ port=args.port,
47
+ workers=args.workers,
48
+ reload=args.reload,
49
+ )
50
+ out_dir = build_server(Path(args.out), args.server_name, options=options)
51
+ print(f"Created Nexom server project at: {out_dir}")
52
+ return
53
+
54
+
55
+ if __name__ == "__main__":
56
+ try:
57
+ main()
58
+ except Exception as e:
59
+ # CLI では stacktrace よりまずメッセージ優先(必要なら後で --verbose とか足す)
60
+ print(f"Error: {e}", file=sys.stderr)
61
+ sys.exit(1)
@@ -0,0 +1,44 @@
1
+ <!DOCTYPE html>
2
+ <html lang="ja">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>__STATUS__</title>
7
+ </head>
8
+ <body>
9
+ <div class="error_container">
10
+ <div class="error_name">__STATUS__</div>
11
+ <div class="error_message">__MESSAGE__</div>
12
+ </div>
13
+ </body>
14
+ </html>
15
+ <style>
16
+ @import url('https://fonts.googleapis.com/css2?family=Google+Sans:ital,opsz,wght@0,17..18,400..700;1,17..18,400..700&family=Noto+Sans+JP:wght@100..900&display=swap');
17
+ body{
18
+ position: relative;
19
+ width: 100%;
20
+ min-width: 300px;
21
+ height: 100vh;
22
+ display: flex;
23
+ justify-content: center;
24
+ align-items: center;
25
+ margin: 0;
26
+
27
+ font-family: 'Noto Sans JP', 'Hiragino Sans', 'ヒラギノ角ゴシック', 'Yu Gothic','游ゴシック';
28
+ background: linear-gradient(135deg, #ffffff, 90%, #ff0050);
29
+ }
30
+
31
+ .error_container{
32
+ position: relative;
33
+ padding: 30px;
34
+ }
35
+ .error_name{
36
+ font-size: 2rem;
37
+ font-family: "Google Sans";
38
+ color: rgb(246, 60, 60);
39
+ }
40
+ .error_message{
41
+ font-size: 1rem;
42
+ font-family: "Google Sans";
43
+ }
44
+ </style>
@@ -0,0 +1,27 @@
1
+ """
2
+ Nexom server settings.
3
+
4
+ This file is generated by Nexom buildTools.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from pathlib import Path
10
+
11
+
12
+ # ======== module ========
13
+ # Project root directory
14
+ directory: str = "{pwd_dir}"
15
+
16
+ # ======== gunicorn ========
17
+ _address: str = "{g_address}"
18
+ _port: int = {g_port}
19
+ _workers: int = {g_workers}
20
+ _reload: bool = {g_reload}
21
+
22
+
23
+ def project_path(*parts: str) -> str:
24
+ """
25
+ Build an absolute path under the project directory.
26
+ """
27
+ return str(Path(directory, *parts))
@@ -0,0 +1,16 @@
1
+
2
+ from __future__ import annotations
3
+
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ # Ensure config.py in the same directory is importable
8
+ ROOT = Path(__file__).resolve().parent
9
+ if str(ROOT) not in sys.path:
10
+ sys.path.insert(0, str(ROOT))
11
+
12
+ from config import _address, _port, _workers, _reload # noqa: E402
13
+
14
+ bind = f"{_address}:{_port}"
15
+ workers = int(_workers)
16
+ reload = bool(_reload)
@@ -0,0 +1,3 @@
1
+ """
2
+ Page handlers for the generated Nexom server project.
3
+ """
@@ -0,0 +1,11 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ from nexom.web.template import ObjectHTMLTemplates
6
+
7
+
8
+ # templates/ directory is located at: <project_root>/templates
9
+ TEMPLATES_DIR = (Path(__file__).resolve().parent.parent / "templates").resolve()
10
+
11
+ templates = ObjectHTMLTemplates(base_dir=str(TEMPLATES_DIR))
@@ -0,0 +1,10 @@
1
+ from nexom.web.request import Request
2
+ from nexom.web.response import Response
3
+
4
+ from ._templates import templates
5
+
6
+
7
+ def main(request: Request, args: dict) -> Response:
8
+ return Response(
9
+ templates.render("default", title="Nexom Default Page")
10
+ )
@@ -0,0 +1,10 @@
1
+ from nexom.web.request import Request
2
+ from nexom.web.response import Response
3
+
4
+ from ._templates import templates
5
+
6
+
7
+ def main(request: Request, args: dict) -> Response:
8
+ return Response(
9
+ templates.document(title="Nexom Documents")
10
+ )
@@ -0,0 +1,18 @@
1
+ from __future__ import annotations
2
+
3
+ import pathlib as plib
4
+
5
+ from nexom.web.path import Path, Static, Pathlib
6
+
7
+ from pages import default, document
8
+
9
+
10
+ # Project root directory (where this file exists)
11
+ ROOT = plib.Path(__file__).resolve().parent
12
+
13
+
14
+ routing = Pathlib(
15
+ Path("", default.main, "DefaultPage"),
16
+ Path("doc/", document.main, "DocumentPage"),
17
+ Static("static/", str(ROOT / "static"), "StaticFiles"),
18
+ )
Binary file
@@ -0,0 +1,39 @@
1
+ body{
2
+ width: 100%;
3
+ padding: 0;
4
+ margin: 0;
5
+ }
6
+
7
+ header{
8
+ width: 100%;
9
+ text-align: center;
10
+ padding-block: 1rem;
11
+ margin-block-end: 1rem;
12
+ }
13
+ .header-title{
14
+ font-size: 20px;
15
+ font-weight: 500;
16
+ }
17
+
18
+ main{
19
+ position: relative;
20
+ width: 600px;
21
+ max-width: 100%;
22
+ margin-inline: auto;
23
+ }
24
+
25
+ footer{
26
+ width: 100%;
27
+ text-align: center;
28
+ padding-block: 1rem;
29
+ margin-block-start: 3rem;
30
+ }
31
+ .footer-text{
32
+ font-size: 0.9rem;
33
+ }
34
+
35
+ pre{
36
+ padding: 0.4rem 0.8rem;
37
+ background-color: rgb(239 239 239);
38
+ border-radius: 0.4rem;
39
+ }
@@ -0,0 +1,18 @@
1
+ <!DOCTYPE html>
2
+ <html lang="ja">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>{{ title }}</title>
7
+ <link rel="stylesheet" href="/static/style.css">
8
+ </head>
9
+ <body>
10
+ <Import header />
11
+
12
+ <main>
13
+ {{ main }}
14
+ </main>
15
+
16
+ <Import footer />
17
+ </body>
18
+ </html>
@@ -0,0 +1,7 @@
1
+ <Extends base />
2
+ <Insert main>
3
+ <h1>Nexom Web Module v1.01 Dev</h1>
4
+ <a href="/doc/">View Doc📕</a>
5
+ <a href="/static/dog.jpeg">View Dog🐕</a>
6
+ </Insert>
7
+
@@ -0,0 +1,169 @@
1
+ <Extends base />
2
+ <Insert main>
3
+ <section id="install">
4
+ <h2>インストール</h2>
5
+ <pre><code>pip install nexom</code></pre>
6
+ <div class="note">
7
+ <strong>前提:</strong> Nexom は WSGI ベースです。WSGI サーバ(例: gunicorn)と組み合わせて動かす想定です。
8
+ </div>
9
+ </section>
10
+
11
+ <section id="quickstart">
12
+ <h2>最短スタート</h2>
13
+ <p>まずは「HTML を返すルート」を 1 本作るのが最速です。</p>
14
+ <h3>ルート定義(例)</h3>
15
+ <pre><code>from nexom.web.path import Path, Pathlib
16
+ from nexom.web.response import HtmlResponse
17
+
18
+ def index(request, args):
19
+ return HtmlResponse("&lt;h1&gt;Hello Nexom&lt;/h1&gt;")
20
+
21
+ routing = Pathlib(
22
+ Path("", index, "index"),
23
+ )</code></pre>
24
+
25
+ <p class="note">
26
+ <strong>ポイント:</strong> Nexom の handler は <code>(request, args)</code> を受け取って <code>Response</code>(または <code>HtmlResponse</code>/<code>JsonResponse</code>)を返すのが基本です。
27
+ </p>
28
+ </section>
29
+
30
+ <section id="routing">
31
+ <h2>ルーティング</h2>
32
+
33
+ <h3>基本</h3>
34
+ <pre><code>from nexom.web.path import Path, Pathlib
35
+
36
+ routing = Pathlib(
37
+ Path("", index, "index"),
38
+ Path("doc/", docs, "docs"),
39
+ )</code></pre>
40
+
41
+ <h3>パスパラメータ({id})</h3>
42
+ <p><code>user/{id}</code> のように書くと、URL の該当部分が <code>args</code> に入ります。</p>
43
+ <pre><code>from nexom.web.response import HtmlResponse
44
+
45
+ def user(request, args):
46
+ return HtmlResponse(f"&lt;p&gt;User ID: {args['id']}&lt;/p&gt;")
47
+
48
+ Path("user/{id}", user, "user")</code></pre>
49
+
50
+ <h3>静的ファイル(Static)</h3>
51
+ <p><code>Static</code> を使うと、ディレクトリ配下のファイルを配信できます。</p>
52
+ <pre><code>from nexom.web.path import Static
53
+
54
+ Static("static/", "./static", "static_files")</code></pre>
55
+ <ul>
56
+ <li><code>/static/xxx</code> を <code>./static/xxx</code> にマップして配信します。</li>
57
+ <li>ディレクトリトラバーサル対策(<code>../</code>)はフレームワーク側でブロックする設計です。</li>
58
+ </ul>
59
+ </section>
60
+
61
+ <section id="responses">
62
+ <h2>レスポンス</h2>
63
+
64
+ <h3>HtmlResponse</h3>
65
+ <pre><code>from nexom.web.response import HtmlResponse
66
+
67
+ return HtmlResponse("&lt;h1&gt;OK&lt;/h1&gt;")</code></pre>
68
+
69
+ <h3>JsonResponse</h3>
70
+ <pre><code>from nexom.web.response import JsonResponse
71
+
72
+ return JsonResponse({"ok": True, "message": "hello"})</code></pre>
73
+
74
+ <h3>低レベル Response(自由度重視)</h3>
75
+ <p>高度なことをしたい場合は <code>Response</code> を直接使います。</p>
76
+ <pre><code>from nexom.web.response import Response
77
+
78
+ # バイナリや任意 Content-Type を返す
79
+ return Response(
80
+ b"binary",
81
+ content_type="application/octet-stream",
82
+ )</code></pre>
83
+
84
+ <h3>リダイレクト</h3>
85
+ <pre><code>from nexom.web.response import Redirect
86
+
87
+ return Redirect("/to")</code></pre>
88
+
89
+ <h3>エラーレスポンス</h3>
90
+ <p>標準のエラーページテンプレートから HTML を生成します。</p>
91
+ <pre><code>from nexom.web.response import ErrorResponse
92
+
93
+ return ErrorResponse(404, "Not Found")</code></pre>
94
+
95
+ <div class="note">
96
+ <strong>charset について:</strong>
97
+ Nexom は <code>str</code> の body を返すときに <code>charset</code> で <code>encode</code> します。<br>
98
+ <code>HtmlResponse</code> / <code>JsonResponse</code> は “短く書ける” 方向で、UTF-8 を前提に使える設計です。
99
+ </div>
100
+ </section>
101
+
102
+ <section id="templates">
103
+ <h2>テンプレート</h2>
104
+ <p>Nexom のテンプレートは「最小機能で予測可能」に寄せた独自構文です。</p>
105
+
106
+ <h3>変数展開</h3>
107
+ <pre><code>&lt;h1&gt;{{ title }}&lt;/h1&gt;</code></pre>
108
+
109
+ <h3>継承(Extends)と差し込み(Insert)</h3>
110
+ <pre><code>&lt;Extends base /&gt;
111
+ &lt;Insert main&gt;
112
+ &lt;h1&gt;Hello&lt;/h1&gt;
113
+ &lt;/Insert&gt;</code></pre>
114
+
115
+ <h3>部品読み込み(Import)</h3>
116
+ <pre><code>&lt;Import header /&gt;</code></pre>
117
+
118
+ <h3>Python 側から呼ぶ</h3>
119
+ <pre><code>from nexom.web.template import Templates
120
+
121
+ templates = Templates("./templates", "default", "document")
122
+
123
+ html = templates.default(title="Nexom")
124
+ return HtmlResponse(html)</code></pre>
125
+
126
+ <div class="note">
127
+ <strong>設計方針:</strong> テンプレートにロジックを入れず、「HTML と差し込み」に寄せることで、コード側をシンプルに保つ思想です。
128
+ </div>
129
+ </section>
130
+
131
+ <section id="cli">
132
+ <h2>CLI</h2>
133
+
134
+ <h3>動作確認</h3>
135
+ <pre><code>python -m nexom test</code></pre>
136
+
137
+ <h3>サーバープロジェクト生成</h3>
138
+ <pre><code>python -m nexom build-server myapp</code></pre>
139
+
140
+ <p>生成されるプロジェクトには以下が含まれます(構成はバージョンにより変わる場合があります)。</p>
141
+ <ul>
142
+ <li>最小のルーティング定義</li>
143
+ <li>テンプレート一式</li>
144
+ <li>静的ファイル用ディレクトリ</li>
145
+ <li>gunicorn / config の雛形</li>
146
+ </ul>
147
+ </section>
148
+
149
+ <section id="tips">
150
+ <h2>運用のコツ</h2>
151
+
152
+ <h3>「短く書く層」と「脱出口」を分ける</h3>
153
+ <ul>
154
+ <li>普段: <code>HtmlResponse</code> / <code>JsonResponse</code> を使う</li>
155
+ <li>詰めたい: <code>Response</code> で headers / content-type を直接制御</li>
156
+ </ul>
157
+
158
+ <h3>ルーティングは “薄く”</h3>
159
+ <p>handler 内で複雑化しそうなら、関数を分けて「処理の塊」を小さくするのが Nexom と相性良いです。</p>
160
+
161
+ <h3>テストのおすすめ</h3>
162
+ <p>公開運用を想定するなら、最低限この3つのテストを用意すると安心です。</p>
163
+ <ul>
164
+ <li>Path の引数抽出</li>
165
+ <li>Static の traversal ブロック</li>
166
+ <li>Response の Content-Type / charset の期待値</li>
167
+ </ul>
168
+ </section>
169
+ </Insert>
@@ -0,0 +1,3 @@
1
+ <footer>
2
+ <div class="footer-text">Nexom Web Framework powerd by AidaTouri 2026</div>
3
+ </footer>
@@ -0,0 +1,3 @@
1
+ <header>
2
+ <div class="header-title">Nexom Web Frameworks</div>
3
+ </header>
@@ -0,0 +1,30 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Callable, Iterable
4
+
5
+ from nexom.web.request import Request
6
+ from nexom.web.response import Response, ErrorResponse
7
+ from nexom.core.error import PathNotFoundError
8
+
9
+ # Project-local router
10
+ from router import routing
11
+
12
+
13
+ def app(environ: dict, start_response: Callable) -> Iterable[bytes]:
14
+ """
15
+ WSGI application entrypoint.
16
+ """
17
+ try:
18
+ request = Request(environ)
19
+ path = request.path
20
+
21
+ p = routing.get(path)
22
+ res = p.call_handler(request)
23
+
24
+ except PathNotFoundError as e:
25
+ res = ErrorResponse(404, str(e))
26
+ except Exception as e:
27
+ res = ErrorResponse(500, str(e))
28
+
29
+ start_response(res.status_text, res.headers)
30
+ return [res.body]
File without changes
@@ -0,0 +1,99 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from importlib import resources
5
+ from pathlib import Path
6
+ import shutil
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class ServerBuildOptions:
11
+ """Options used to fill generated config.py."""
12
+ address: str = "0.0.0.0"
13
+ port: int = 8080
14
+ workers: int = 4
15
+ reload: bool = False
16
+
17
+
18
+ def _copy_from_package(pkg: str, filename: str, dest: Path) -> None:
19
+ """
20
+ Copy a file from a package resource into the destination path.
21
+ """
22
+ dest.parent.mkdir(parents=True, exist_ok=True)
23
+ with resources.files(pkg).joinpath(filename).open("rb") as src, dest.open("wb") as dst:
24
+ shutil.copyfileobj(src, dst)
25
+
26
+
27
+ def server(work_dir: str | Path, name: str, *, options: ServerBuildOptions | None = None) -> Path:
28
+ """
29
+ Generate a Nexom server project into `work_dir`.
30
+
31
+ This function copies template files bundled in the package (assets) and
32
+ writes a ready-to-run config.py.
33
+
34
+ Args:
35
+ work_dir: Output directory where project files are created.
36
+ name: Project name (reserved for future use; currently not used).
37
+ options: Config defaults for generated config.py.
38
+
39
+ Returns:
40
+ The absolute path to the generated project directory.
41
+
42
+ Raises:
43
+ FileExistsError: If target directories/files already exist.
44
+ ModuleNotFoundError / FileNotFoundError: If bundled assets are missing.
45
+ """
46
+ _ = name # reserved (keep signature stable for future)
47
+ options = options or ServerBuildOptions()
48
+
49
+ out_dir = Path(work_dir).expanduser().resolve()
50
+ out_dir.mkdir(parents=True, exist_ok=True)
51
+
52
+ pages_dir = out_dir / "pages"
53
+ templates_dir = out_dir / "templates"
54
+ static_dir = out_dir / "static"
55
+
56
+ # Make sure we don't accidentally overwrite a project
57
+ for d in (pages_dir, templates_dir, static_dir):
58
+ if d.exists():
59
+ raise FileExistsError(f"Already exists: {d}")
60
+
61
+ pages_dir.mkdir()
62
+ templates_dir.mkdir()
63
+ static_dir.mkdir()
64
+
65
+ # ---- Copy pages ----
66
+ pages_pkg = "nexom.assets.server.pages"
67
+ for fn in ("__init__.py", "_templates.py", "default.py", "document.py"):
68
+ _copy_from_package(pages_pkg, fn, pages_dir / fn)
69
+
70
+ # ---- Copy templates ----
71
+ templates_pkg = "nexom.assets.server.templates"
72
+ for fn in ("base.html", "header.html", "footer.html", "default.html", "document.html"):
73
+ _copy_from_package(templates_pkg, fn, templates_dir / fn)
74
+
75
+ # ---- Copy static ----
76
+ static_pkg = "nexom.assets.server.static"
77
+ for fn in ("dog.jpeg", "style.css"):
78
+ _copy_from_package(static_pkg, fn, static_dir / fn)
79
+
80
+ # ---- Copy app files ----
81
+ app_pkg = "nexom.assets.server"
82
+ for fn in ("gunicorn.conf.py", "router.py", "wsgi.py", "config.py"):
83
+ _copy_from_package(app_pkg, fn, out_dir / fn)
84
+
85
+ # ---- Enable settings (format config.py) ----
86
+ config_path = out_dir / "config.py"
87
+ config_text = config_path.read_text(encoding="utf-8")
88
+
89
+ # NOTE: pwd_dir should be the generated project directory, not current cwd.
90
+ enabled = config_text.format(
91
+ pwd_dir=str(out_dir),
92
+ g_address=options.address,
93
+ g_port=options.port,
94
+ g_workers=options.workers,
95
+ g_reload=options.reload,
96
+ )
97
+ config_path.write_text(enabled, encoding="utf-8")
98
+
99
+ return out_dir
nexom/core/__init__.py ADDED
@@ -0,0 +1 @@
1
+ from . import error