nautica 3.0.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.
Files changed (49) hide show
  1. nautica-3.0.0/PKG-INFO +112 -0
  2. nautica-3.0.0/README.md +94 -0
  3. nautica-3.0.0/napi/__init__.py +1 -0
  4. nautica-3.0.0/napi/http.py +91 -0
  5. nautica-3.0.0/nautica/__init__.py +12 -0
  6. nautica-3.0.0/nautica/__main__.py +9 -0
  7. nautica-3.0.0/nautica/cli/Create.py +49 -0
  8. nautica-3.0.0/nautica/cli/Install.py +35 -0
  9. nautica-3.0.0/nautica/cli/Run.py +25 -0
  10. nautica-3.0.0/nautica/cli/__init__.py +17 -0
  11. nautica-3.0.0/nautica/ext/Static.py +268 -0
  12. nautica-3.0.0/nautica/ext/StatusCodes.py +157 -0
  13. nautica-3.0.0/nautica/ext/Util.py +103 -0
  14. nautica-3.0.0/nautica/ext/__init__.py +0 -0
  15. nautica-3.0.0/nautica/manager/__init__.py +9 -0
  16. nautica-3.0.0/nautica/manager/config/__init__.py +72 -0
  17. nautica-3.0.0/nautica/manager/config/builder.py +26 -0
  18. nautica-3.0.0/nautica/manager/config/helper.py +89 -0
  19. nautica-3.0.0/nautica/manager/logger/__init__.py +193 -0
  20. nautica-3.0.0/nautica/manager/logger/levels.py +30 -0
  21. nautica-3.0.0/nautica/manager/logger/tableutil.py +45 -0
  22. nautica-3.0.0/nautica/manager/memory/__init__.py +52 -0
  23. nautica-3.0.0/nautica/models/Http.py +216 -0
  24. nautica-3.0.0/nautica/models/Requirements.py +117 -0
  25. nautica-3.0.0/nautica/models/Service.py +62 -0
  26. nautica-3.0.0/nautica/models/Shell.py +92 -0
  27. nautica-3.0.0/nautica/services/__init__.py +132 -0
  28. nautica-3.0.0/nautica/services/builtins/__init__.py +36 -0
  29. nautica-3.0.0/nautica/services/builtins/http/__init__.py +36 -0
  30. nautica-3.0.0/nautica/services/builtins/http/middleware.py +167 -0
  31. nautica-3.0.0/nautica/services/builtins/http/requirements.py +138 -0
  32. nautica-3.0.0/nautica/services/builtins/http/router.py +61 -0
  33. nautica-3.0.0/nautica/services/builtins/http/server.py +97 -0
  34. nautica-3.0.0/nautica/services/builtins/shell/__init__.py +169 -0
  35. nautica-3.0.0/nautica/services/builtins/shell/commands/basic.py +106 -0
  36. nautica-3.0.0/nautica/services/builtins/shell/decorator.py +18 -0
  37. nautica-3.0.0/nautica/services/builtins/shell/gui/__init__.py +55 -0
  38. nautica-3.0.0/nautica/services/builtins/shell/gui/autocomplete.py +103 -0
  39. nautica-3.0.0/nautica/services/builtins/shell/gui/css.py +132 -0
  40. nautica-3.0.0/nautica/services/builtins/shell/gui/pages/home.py +191 -0
  41. nautica-3.0.0/nautica/services/builtins/shell/gui/themes.py +53 -0
  42. nautica-3.0.0/nautica.egg-info/PKG-INFO +112 -0
  43. nautica-3.0.0/nautica.egg-info/SOURCES.txt +47 -0
  44. nautica-3.0.0/nautica.egg-info/dependency_links.txt +1 -0
  45. nautica-3.0.0/nautica.egg-info/entry_points.txt +2 -0
  46. nautica-3.0.0/nautica.egg-info/requires.txt +8 -0
  47. nautica-3.0.0/nautica.egg-info/top_level.txt +2 -0
  48. nautica-3.0.0/pyproject.toml +35 -0
  49. nautica-3.0.0/setup.cfg +4 -0
nautica-3.0.0/PKG-INFO ADDED
@@ -0,0 +1,112 @@
1
+ Metadata-Version: 2.4
2
+ Name: nautica
3
+ Version: 3.0.0
4
+ Summary: A service management framework
5
+ Author-email: Xellu <xellu@catboys.cc>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/xellu/nautica-api
8
+ Requires-Python: >=3.11
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: starlette
11
+ Requires-Dist: uvicorn
12
+ Requires-Dist: python-multipart
13
+ Requires-Dist: colorama
14
+ Requires-Dist: tomlkit
15
+ Requires-Dist: click
16
+ Requires-Dist: psutil
17
+ Requires-Dist: textual
18
+
19
+ # Nautica V3
20
+
21
+ Nautica V3 _(also referred to as Nautica API, Nautica3, N3)_ is a modular application framework built around a service registry. It provides you with a structured way to manage components of your applications, such as HTTP or Socket servers, background workers, etc.
22
+
23
+ > N3 is not just an HTTP framework — the HTTP server is one of many services. It's designed for applications that need to coordinate multiple components at once.
24
+
25
+ ## What's Included
26
+ - **Service Registry** with dependency resolver
27
+ - **Lifecycle hooks** for install, start and shutdown
28
+ - TOML **Config system** with key management
29
+ - **Logger** with file output and memory
30
+ - **Shell** for interacting with services
31
+ - **TUI** for live logs, thread and worker inspection _(optional)_
32
+ - **Plugin system** for extending projects without modifying core code
33
+ - **HTTP API** built on Starlette + Uvicorn
34
+
35
+ ---
36
+
37
+ I made N3 because I was solving the same problems in my projects, those being: configs, logging, figuring out startup orders, not to mention the validation boilerplate on every route. Because of this I made Nautica V2, which solved many of these issues, and V3 to improve on the idea.
38
+
39
+ ## How HTTP API Compares
40
+
41
+ ### Benchmark Performance
42
+
43
+ | Library | Requests/Second | Avg Latency | Overall |
44
+ | --- | --- | --- | --- |
45
+ | Nautica3 | `2271` | `4.4ms` | - |
46
+ | FastAPI | `2259` | `4.4ms` | 0.5% slower |
47
+ | Flask | `75` | `132.6ms` | 96% slower |
48
+
49
+ *Ran with 10 workers, for 10 seconds. Nautica3 matches FastAPI despite running additional middleware, requirement parsing, and logging on every request.*
50
+
51
+ ### Code Complexity
52
+
53
+ Similarly to SvelteKit, Nautica defines the route names for you. This helps to reduce boilerplate (see Flask and FastAPI examples), improve readability, and helps with naming conventions
54
+
55
+ #### Nautica3
56
+ ```py
57
+ # file: src/http/api/v1/auth.py
58
+ from napi.http import HTTP, Context, Reply
59
+ from somewhere import username, password
60
+
61
+ @HTTP.POST()
62
+ @HTTP.Require(body = {"username": str, "password": str})
63
+ def login(ctx: Context):
64
+ if ctx.body["username"] == username and ctx.body["password"] == password:
65
+ return Reply(ok=True)
66
+ return Reply(ok=False, error="Invalid credentials"), 401
67
+ ```
68
+
69
+ #### Flask
70
+ ```py
71
+ # file: main.py
72
+ from flask import Flask, request
73
+ from flask.blueprints import Blueprint
74
+ import json
75
+ from somewhere import username, password
76
+
77
+ app = Flask(__name__)
78
+ v1auth = Blueprint("v1auth", __name__, url_prefix="/api/v1/auth")
79
+
80
+ @v1auth.post("/login")
81
+ def login():
82
+ data = request.get_json(silent=True) or {}
83
+ if data.get("username") == username and data.get("password") == password:
84
+ return json.dumps({"ok": True})
85
+ return json.dumps({"ok": False, "error": "Invalid credentials"}), 401
86
+
87
+ app.register_blueprint(v1auth)
88
+ app.run(port=8101)
89
+ ```
90
+
91
+ #### FastAPI
92
+ ```py
93
+ # file: main.py
94
+ from fastapi import FastAPI, HTTPException
95
+ from pydantic import BaseModel
96
+ import uvicorn
97
+ from somewhere import username, password
98
+
99
+ app = FastAPI()
100
+
101
+ class LoginRequest(BaseModel):
102
+ username: str
103
+ password: str
104
+
105
+ @app.post("/api/v1/auth/login")
106
+ def login(body: LoginRequest):
107
+ if body.username == username and body.password == password:
108
+ return {"ok": True}
109
+ raise HTTPException(status_code=401, detail="Invalid credentials")
110
+
111
+ uvicorn.run(app, port=8101)
112
+ ```
@@ -0,0 +1,94 @@
1
+ # Nautica V3
2
+
3
+ Nautica V3 _(also referred to as Nautica API, Nautica3, N3)_ is a modular application framework built around a service registry. It provides you with a structured way to manage components of your applications, such as HTTP or Socket servers, background workers, etc.
4
+
5
+ > N3 is not just an HTTP framework — the HTTP server is one of many services. It's designed for applications that need to coordinate multiple components at once.
6
+
7
+ ## What's Included
8
+ - **Service Registry** with dependency resolver
9
+ - **Lifecycle hooks** for install, start and shutdown
10
+ - TOML **Config system** with key management
11
+ - **Logger** with file output and memory
12
+ - **Shell** for interacting with services
13
+ - **TUI** for live logs, thread and worker inspection _(optional)_
14
+ - **Plugin system** for extending projects without modifying core code
15
+ - **HTTP API** built on Starlette + Uvicorn
16
+
17
+ ---
18
+
19
+ I made N3 because I was solving the same problems in my projects, those being: configs, logging, figuring out startup orders, not to mention the validation boilerplate on every route. Because of this I made Nautica V2, which solved many of these issues, and V3 to improve on the idea.
20
+
21
+ ## How HTTP API Compares
22
+
23
+ ### Benchmark Performance
24
+
25
+ | Library | Requests/Second | Avg Latency | Overall |
26
+ | --- | --- | --- | --- |
27
+ | Nautica3 | `2271` | `4.4ms` | - |
28
+ | FastAPI | `2259` | `4.4ms` | 0.5% slower |
29
+ | Flask | `75` | `132.6ms` | 96% slower |
30
+
31
+ *Ran with 10 workers, for 10 seconds. Nautica3 matches FastAPI despite running additional middleware, requirement parsing, and logging on every request.*
32
+
33
+ ### Code Complexity
34
+
35
+ Similarly to SvelteKit, Nautica defines the route names for you. This helps to reduce boilerplate (see Flask and FastAPI examples), improve readability, and helps with naming conventions
36
+
37
+ #### Nautica3
38
+ ```py
39
+ # file: src/http/api/v1/auth.py
40
+ from napi.http import HTTP, Context, Reply
41
+ from somewhere import username, password
42
+
43
+ @HTTP.POST()
44
+ @HTTP.Require(body = {"username": str, "password": str})
45
+ def login(ctx: Context):
46
+ if ctx.body["username"] == username and ctx.body["password"] == password:
47
+ return Reply(ok=True)
48
+ return Reply(ok=False, error="Invalid credentials"), 401
49
+ ```
50
+
51
+ #### Flask
52
+ ```py
53
+ # file: main.py
54
+ from flask import Flask, request
55
+ from flask.blueprints import Blueprint
56
+ import json
57
+ from somewhere import username, password
58
+
59
+ app = Flask(__name__)
60
+ v1auth = Blueprint("v1auth", __name__, url_prefix="/api/v1/auth")
61
+
62
+ @v1auth.post("/login")
63
+ def login():
64
+ data = request.get_json(silent=True) or {}
65
+ if data.get("username") == username and data.get("password") == password:
66
+ return json.dumps({"ok": True})
67
+ return json.dumps({"ok": False, "error": "Invalid credentials"}), 401
68
+
69
+ app.register_blueprint(v1auth)
70
+ app.run(port=8101)
71
+ ```
72
+
73
+ #### FastAPI
74
+ ```py
75
+ # file: main.py
76
+ from fastapi import FastAPI, HTTPException
77
+ from pydantic import BaseModel
78
+ import uvicorn
79
+ from somewhere import username, password
80
+
81
+ app = FastAPI()
82
+
83
+ class LoginRequest(BaseModel):
84
+ username: str
85
+ password: str
86
+
87
+ @app.post("/api/v1/auth/login")
88
+ def login(body: LoginRequest):
89
+ if body.username == username and body.password == password:
90
+ return {"ok": True}
91
+ raise HTTPException(status_code=401, detail="Invalid credentials")
92
+
93
+ uvicorn.run(app, port=8101)
94
+ ```
@@ -0,0 +1 @@
1
+ from . import http
@@ -0,0 +1,91 @@
1
+
2
+ from nautica.manager import Logger
3
+
4
+ from nautica.services.builtins.http.middleware import Middleware
5
+ from nautica.models.Http import RouteRequirements, RequestContext as Context, Reply, ErrorReply as Error
6
+ from nautica.models import Requirements
7
+ from nautica.ext import StatusCodes
8
+
9
+ from starlette.responses import JSONResponse, PlainTextResponse, FileResponse, HTMLResponse, RedirectResponse, StreamingResponse
10
+
11
+ Require = Requirements
12
+
13
+ class RouteManager:
14
+ def __init__(self):
15
+ self.temp = []
16
+
17
+ def _create(self, r):
18
+ self.temp.append(r)
19
+ Logger.debug(f"Registered route for {r.func.__name__}, {r.method=}, {r.name=}")
20
+
21
+ def GET(self, name: str | None = None):
22
+ return Middleware(self, "get", name).decorator
23
+
24
+ def POST(self, name: str | None = None):
25
+ return Middleware(self, "post", name).decorator
26
+
27
+ def HEAD(self, name: str | None = None):
28
+ return Middleware(self, "head", name).decorator
29
+
30
+ def PUT(self, name: str | None = None):
31
+ return Middleware(self, "put", name).decorator
32
+
33
+ def DELETE(self, name: str | None = None):
34
+ return Middleware(self, "delete", name).decorator
35
+
36
+ def CONNECT(self, name: str | None = None):
37
+ return Middleware(self, "connect", name).decorator
38
+
39
+ def TRACE(self, name: str | None = None):
40
+ return Middleware(self, "trace", name).decorator
41
+
42
+ def PATCH(self, name: str | None = None):
43
+ return Middleware(self, "patch", name).decorator
44
+
45
+ def Require(self,
46
+ body: dict = None,
47
+ headers: dict = None,
48
+ cookies: dict = None,
49
+ query: dict = None,
50
+ files: dict = None
51
+ ):
52
+ for field in [body or {}, headers or {}, cookies or {}, query or {}]:
53
+ for v in field.values():
54
+ if not (isinstance(v, type) or isinstance(v, Requirements.Requirement)): raise TypeError(f"Context builder only accepts types and Requirements")
55
+
56
+ for v in (files or {}).values():
57
+ if not isinstance(v, Requirements.File): raise TypeError(f"File dict only accepts Requirements.File, provided '{type(v).__name__}'")
58
+
59
+ def decorator(func):
60
+ func._requirements = RouteRequirements(
61
+ body=body,
62
+ headers=headers,
63
+ cookies=cookies,
64
+ query=query,
65
+ files = files
66
+ )
67
+ return func
68
+
69
+ return decorator
70
+
71
+ def Before(self, fn):
72
+ def decorator(func):
73
+ if not hasattr(fn, "_before"):
74
+ fn._before = []
75
+ fn._before.append(func)
76
+ return func
77
+ return decorator
78
+
79
+ def After(self, fn):
80
+ def decorator(func):
81
+ if not hasattr(fn, "_after"):
82
+ fn._after = []
83
+ fn._after.append(func)
84
+ return func
85
+ return decorator
86
+
87
+
88
+
89
+
90
+
91
+ HTTP = Router = RouteManager()
@@ -0,0 +1,12 @@
1
+ from .ext.Static import RELEASE, EDITION
2
+ from .ext import Util
3
+
4
+ from .manager import Logger, LogLevel
5
+ from .manager import Config, ConfigBuilder
6
+
7
+ from .services import Service, Services
8
+
9
+ from . import (
10
+ models,
11
+ ext
12
+ )
@@ -0,0 +1,9 @@
1
+ from .cli import cli
2
+ from .cli import (
3
+ Create,
4
+ Install,
5
+ Run
6
+ )
7
+
8
+ if __name__ == "__main__":
9
+ cli()
@@ -0,0 +1,49 @@
1
+ from . import cli, click
2
+
3
+ import os
4
+ from colorama import Fore
5
+
6
+ from ..ext.Static import banner, GitIgnore, ProjectExample
7
+ from ..ext.Util import walkPath
8
+
9
+ from ..manager import Logger, LogLevel
10
+
11
+ from ..services import Registry
12
+
13
+ @cli.command()
14
+ @click.argument("name", type=str)
15
+ def create(name):
16
+ print(f"{Fore.BLUE}{banner()}{Fore.RESET}")
17
+ if os.path.exists(name) and len(walkPath(name, include_dirs=True)) > 0:
18
+ Logger.error(f"A Non-empty directory with this name already exists")
19
+ return
20
+
21
+ #prep working directory
22
+ Logger.info("Creating project directories...")
23
+ os.makedirs(name, exist_ok=True)
24
+ os.chdir(name)
25
+
26
+ for f in [".logs", "config", "plugins", "src/http"]:
27
+ os.makedirs(f, exist_ok=True)
28
+
29
+ with open(".gitignore", "w") as f: f.write(GitIgnore)
30
+ with open("src/http/+root.py", "w") as f: f.write(ProjectExample)
31
+
32
+ Logger.ok("Created project tree")
33
+
34
+ #install services
35
+ Logger.info("Installing services...")
36
+
37
+ Registry.ImportAll()
38
+ Registry.onInstall()
39
+
40
+ Logger.ok("Services Installed")
41
+
42
+ #clean up
43
+ os.chdir("..")
44
+
45
+ Logger.table() \
46
+ .labels(["Project Created! Get Started by running:"]) \
47
+ .row([f"cd {name}"]).row(["nautica install"]).row(["nautica run ."]) \
48
+ .row([""]).row(["Thank you for using Nautica3!"]) \
49
+ .display(LogLevel.DEBUG)
@@ -0,0 +1,35 @@
1
+ from . import cli
2
+
3
+ import os
4
+ from colorama import Fore
5
+
6
+ from ..ext.Static import banner
7
+
8
+ from ..manager import Logger, LogLevel
9
+ from ..manager.config import ROOT_CONFIGS
10
+
11
+ from ..services import Registry
12
+
13
+ @cli.command(aliases=["i"])
14
+ def install():
15
+ print(f"{Fore.BLUE}{banner()}{Fore.RESET}")
16
+
17
+ Logger.info("Validating project configuration...")
18
+ for path in ROOT_CONFIGS.values():
19
+ if not os.path.exists(path):
20
+ Logger.error(f"Project config '{path}' is missing!")
21
+ return
22
+
23
+ #add download sequence whenever i add package manager
24
+
25
+ #install services
26
+ Logger.info("Installing services...")
27
+
28
+ Registry.ImportAll()
29
+ Registry.onInstall()
30
+
31
+ Logger.ok("Services Installed")
32
+
33
+ Logger.table() \
34
+ .labels(["Services Installed"]) \
35
+ .display(LogLevel.DEBUG)
@@ -0,0 +1,25 @@
1
+ from . import cli, click
2
+
3
+ import os
4
+ from colorama import Fore
5
+
6
+ from ..ext.Static import banner
7
+
8
+ from ..manager import Logger, LogLevel
9
+ from ..manager.config import ROOT_CONFIGS
10
+
11
+ from ..services import Services
12
+
13
+ @cli.command()
14
+ @click.argument("path", type=str)
15
+ def run(path: str = "."):
16
+ print(f"{Fore.BLUE}{banner()}{Fore.RESET}")
17
+
18
+ Logger.info("Validating project configuration...")
19
+ for path in ROOT_CONFIGS.values():
20
+ if not os.path.exists(path):
21
+ Logger.error(f"Project config '{path}' is missing!")
22
+ return
23
+
24
+ Services.ImportAll()
25
+ Services.onStart()
@@ -0,0 +1,17 @@
1
+ import os
2
+ import click
3
+
4
+
5
+ class AliasedGroup(click.Group):
6
+ def command(self, *args, aliases: list[str] = None, **kwargs):
7
+ decorator = super().command(*args, **kwargs)
8
+ def wrapper(func):
9
+ cmd = decorator(func)
10
+ for alias in (aliases or []):
11
+ self.add_command(cmd, name=alias)
12
+ return cmd
13
+ return wrapper
14
+
15
+
16
+ @click.group(cls=AliasedGroup)
17
+ def cli(): ...