Skip to content

Commit 543d9c9

Browse files
committed
Fix relative imports to get CLI working again
1 parent cf9bbb0 commit 543d9c9

15 files changed

+57
-54
lines changed

pyninja/__init__.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@
55

66
import click
77

8-
from pyninja.main import start # noqa: F401
9-
10-
version = "0.0.4"
8+
from .main import start, version
119

1210

1311
@click.command()
@@ -50,7 +48,7 @@ def commandline(*args, **kwargs) -> None:
5048
for k, v in options.items()
5149
)
5250
if kwargs.get("version"):
53-
click.echo(f"PyNinja {version}")
51+
click.echo(f"PyNinja {version.__version__}")
5452
sys.exit(0)
5553
if kwargs.get("help"):
5654
click.echo(

pyninja/auth.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from fastapi import Request
88
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
99

10-
from pyninja import database, exceptions, models
10+
from . import database, exceptions, models
1111

1212
LOGGER = logging.getLogger("uvicorn.default")
1313
EPOCH = lambda: int(time.time()) # noqa: E731

pyninja/database.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from pyninja import models
1+
from . import models
22

33

44
def get_record(host: str) -> int | None:

pyninja/main.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
from fastapi import FastAPI, Request
55
from fastapi.responses import JSONResponse, RedirectResponse
66

7-
import pyninja
8-
from pyninja import exceptions, models, monitor, routers, squire
7+
from . import exceptions, models, routers, squire, version
8+
from .monitor.config import static
99

1010
LOGGER = logging.getLogger("uvicorn.default")
1111

@@ -25,7 +25,7 @@ async def redirect_exception_handler(
2525
"""
2626
LOGGER.debug("Exception headers: %s", request.headers)
2727
LOGGER.debug("Exception cookies: %s", request.cookies)
28-
if request.url.path == monitor.config.static.login_endpoint:
28+
if request.url.path == static.login_endpoint:
2929
response = JSONResponse(
3030
content={"redirect_url": exception.location}, status_code=200
3131
)
@@ -68,7 +68,7 @@ def start(**kwargs) -> None:
6868
routes=routers.get_all_routes(),
6969
title="PyNinja",
7070
description="Lightweight OS-agnostic service monitoring API",
71-
version=pyninja.version,
71+
version=version.__version__,
7272
)
7373
app.add_exception_handler(
7474
exc_class_or_status_code=exceptions.RedirectException,

pyninja/models.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from pydantic_core import InitErrorDetails
1717
from pydantic_settings import BaseSettings
1818

19-
from pyninja import exceptions
19+
from . import exceptions
2020

2121
OPERATING_SYSTEM = platform.system()
2222

pyninja/monitor/__init__.py

+1-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
1-
import os
21
from typing import List
32

43
from fastapi import Depends
54
from fastapi.routing import APIRoute, APIWebSocketRoute
6-
from fastapi.templating import Jinja2Templates
75

8-
from pyninja.monitor import authenticator, config, routes, secure # noqa: F401
9-
10-
templates = Jinja2Templates(
11-
directory=os.path.join(os.path.dirname(__file__), "templates")
12-
)
6+
from . import authenticator, config, routes, secure # noqa: F401
137

148

159
def get_all_monitor_routes(

pyninja/monitor/authenticator.py

+11-11
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
from fastapi import Request, status
99
from fastapi.responses import HTMLResponse
1010

11-
import pyninja
12-
from pyninja import exceptions, models, monitor, squire
11+
from .. import exceptions, models, squire, version
12+
from . import config, secure
1313

1414
LOGGER = logging.getLogger("uvicorn.default")
1515

@@ -55,9 +55,9 @@ async def extract_credentials(authorization: str, host: str) -> List[str]:
5555
"""
5656
if not authorization:
5757
await raise_error(host)
58-
decoded_auth = await monitor.secure.base64_decode(authorization)
58+
decoded_auth = await secure.base64_decode(authorization)
5959
# convert hex to a string
60-
auth = await monitor.secure.hex_decode(decoded_auth)
60+
auth = await secure.hex_decode(decoded_auth)
6161
return auth.split(",")
6262

6363

@@ -70,13 +70,13 @@ async def verify_login(authorization: str, host: str) -> Dict[str, Union[str, in
7070
"""
7171
username, signature, timestamp = await extract_credentials(authorization, host)
7272
if secrets.compare_digest(username, models.env.monitor_username):
73-
hex_user = await monitor.secure.hex_encode(models.env.monitor_username)
74-
hex_pass = await monitor.secure.hex_encode(models.env.monitor_password)
73+
hex_user = await secure.hex_encode(models.env.monitor_username)
74+
hex_pass = await secure.hex_encode(models.env.monitor_password)
7575
else:
7676
LOGGER.warning("User '%s' not allowed", username)
7777
await raise_error(host)
7878
message = f"{hex_user}{hex_pass}{timestamp}"
79-
expected_signature = await monitor.secure.calculate_hash(message)
79+
expected_signature = await secure.calculate_hash(message)
8080
if secrets.compare_digest(signature, expected_signature):
8181
models.ws_session.invalid[host] = 0
8282
key = squire.keygen()
@@ -97,7 +97,7 @@ async def generate_cookie(auth_payload: dict) -> Dict[str, str | bool | int]:
9797
Dict[str, str | bool | int]:
9898
Returns a dictionary with cookie details
9999
"""
100-
expiration = await monitor.config.get_expiry(
100+
expiration = await config.get_expiry(
101101
lease_start=auth_payload["timestamp"], lease_duration=models.env.monitor_session
102102
)
103103
LOGGER.info(
@@ -128,13 +128,13 @@ async def session_error(
128128
HTMLResponse:
129129
Returns an HTML response templated using Jinja2.
130130
"""
131-
return monitor.templates.TemplateResponse(
131+
return config.templates.TemplateResponse(
132132
name="session.html",
133133
context={
134134
"request": request,
135-
"signin": monitor.config.static.login_endpoint,
135+
"signin": config.static.login_endpoint,
136136
"reason": error.detail,
137-
"version": f"v{pyninja.version}",
137+
"version": f"v{version.__version__}",
138138
},
139139
)
140140

pyninja/monitor/config.py

+6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1+
import os
12
import time
23

34
from fastapi.responses import HTMLResponse
5+
from fastapi.templating import Jinja2Templates
46
from pydantic import BaseModel
57

8+
templates = Jinja2Templates(
9+
directory=os.path.join(os.path.dirname(__file__), "templates")
10+
)
11+
612

713
async def clear_session(response: HTMLResponse) -> HTMLResponse:
814
"""Clear the session token from the response.

pyninja/monitor/routes.py

+16-12
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
from fastapi.responses import HTMLResponse, JSONResponse
88
from fastapi.websockets import WebSocket, WebSocketDisconnect, WebSocketState
99

10-
import pyninja
11-
from pyninja import exceptions, models, monitor, squire
10+
from .. import exceptions, models, monitor, squire, version
1211

1312
LOGGER = logging.getLogger("uvicorn.default")
1413

@@ -24,12 +23,12 @@ async def error_endpoint(request: Request) -> HTMLResponse:
2423
Returns an HTML response templated using Jinja2.
2524
"""
2625
return await monitor.config.clear_session(
27-
monitor.templates.TemplateResponse(
26+
monitor.config.templates.TemplateResponse(
2827
name="unauthorized.html",
2928
context={
3029
"request": request,
3130
"signin": monitor.config.static.login_endpoint,
32-
"version": f"v{pyninja.version}",
31+
"version": f"v{version.__version__}",
3332
},
3433
)
3534
)
@@ -52,14 +51,14 @@ async def logout_endpoint(request: Request) -> HTMLResponse:
5251
response = await monitor.authenticator.session_error(request, error)
5352
else:
5453
models.ws_session.client_auth.pop(request.client.host)
55-
response = monitor.templates.TemplateResponse(
54+
response = monitor.config.templates.TemplateResponse(
5655
name="logout.html",
5756
context={
5857
"request": request,
5958
"detail": "Session Expired",
6059
"signin": monitor.config.static.login_endpoint,
6160
"show_login": True,
62-
"version": f"v{pyninja.version}",
61+
"version": f"v{version.__version__}",
6362
},
6463
)
6564
return await monitor.config.clear_session(response)
@@ -109,7 +108,7 @@ async def monitor_endpoint(request: Request, session_token: str = Cookie(None)):
109108
await monitor.authenticator.session_error(request, error)
110109
)
111110
else:
112-
return monitor.templates.TemplateResponse(
111+
return monitor.config.templates.TemplateResponse(
113112
name="main.html",
114113
context=dict(
115114
request=request,
@@ -118,12 +117,12 @@ async def monitor_endpoint(request: Request, session_token: str = Cookie(None)):
118117
),
119118
)
120119
else:
121-
return monitor.templates.TemplateResponse(
120+
return monitor.config.templates.TemplateResponse(
122121
name="index.html",
123122
context={
124123
"request": request,
125124
"signin": monitor.config.static.login_endpoint,
126-
"version": f"v{pyninja.version}",
125+
"version": f"v{version.__version__}",
127126
},
128127
)
129128

@@ -136,9 +135,14 @@ async def websocket_endpoint(websocket: WebSocket, session_token: str = Cookie(N
136135
session_token: Session token set after verifying username and password.
137136
"""
138137
await websocket.accept()
139-
session_validity = await monitor.authenticator.validate_session(
140-
websocket.client.host, session_token
141-
)
138+
try:
139+
session_validity = await monitor.authenticator.validate_session(
140+
websocket.client.host, session_token
141+
)
142+
except exceptions.SessionError as error:
143+
await websocket.send_text(error.__str__())
144+
await websocket.close()
145+
return
142146
if not session_validity:
143147
await websocket.send_text("Unauthorized")
144148
await websocket.close()

pyninja/rate_limit.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from fastapi import Request
66

7-
from pyninja import exceptions, models
7+
from . import exceptions, models
88

99

1010
class RateLimiter:

pyninja/routers.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from fastapi.security import HTTPAuthorizationCredentials, HTTPBasic, HTTPBearer
1111
from pydantic import PositiveFloat, PositiveInt
1212

13-
from pyninja import (
13+
from . import (
1414
auth,
1515
dockerized,
1616
exceptions,

pyninja/service.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import subprocess
33
from http import HTTPStatus
44

5-
from pyninja import models
5+
from . import models
66

77
LOGGER = logging.getLogger("uvicorn.default")
88

pyninja/squire.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import yaml
1515
from pydantic import PositiveFloat, PositiveInt
1616

17-
from pyninja.models import EnvConfig
17+
from . import models
1818

1919
LOGGER = logging.getLogger("uvicorn.default")
2020
IP_REGEX = re.compile(
@@ -146,7 +146,7 @@ def process_command(
146146
return result
147147

148148

149-
def envfile_loader(filename: str | os.PathLike) -> EnvConfig:
149+
def envfile_loader(filename: str | os.PathLike) -> models.EnvConfig:
150150
"""Loads environment variables based on filetypes.
151151
152152
Args:
@@ -160,24 +160,24 @@ def envfile_loader(filename: str | os.PathLike) -> EnvConfig:
160160
if env_file.suffix.lower() == ".json":
161161
with open(env_file) as stream:
162162
env_data = json.load(stream)
163-
return EnvConfig(**{k.lower(): v for k, v in env_data.items()})
163+
return models.EnvConfig(**{k.lower(): v for k, v in env_data.items()})
164164
elif env_file.suffix.lower() in (".yaml", ".yml"):
165165
with open(env_file) as stream:
166166
env_data = yaml.load(stream, yaml.FullLoader)
167-
return EnvConfig(**{k.lower(): v for k, v in env_data.items()})
167+
return models.EnvConfig(**{k.lower(): v for k, v in env_data.items()})
168168
elif not env_file.suffix or env_file.suffix.lower() in (
169169
".text",
170170
".txt",
171171
"",
172172
):
173-
return EnvConfig.from_env_file(env_file)
173+
return models.EnvConfig.from_env_file(env_file)
174174
else:
175175
raise ValueError(
176176
"\n\tUnsupported format for 'env_file', can be one of (.json, .yaml, .yml, .txt, .text, or null)"
177177
)
178178

179179

180-
def load_env(**kwargs) -> EnvConfig:
180+
def load_env(**kwargs) -> models.EnvConfig:
181181
"""Merge env vars from env_file with kwargs, giving priority to kwargs.
182182
183183
See Also:
@@ -194,7 +194,7 @@ def load_env(**kwargs) -> EnvConfig:
194194
else:
195195
file_env = {}
196196
merged_env = {**file_env, **kwargs}
197-
return EnvConfig(**merged_env)
197+
return models.EnvConfig(**merged_env)
198198

199199

200200
def keygen() -> str:

pyninja/version.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__version__ = "0.0.4"

pyproject.toml

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ keywords = ["service-monitor", "PyNinja"]
1818
requires-python = ">=3.10"
1919

2020
[tool.setuptools]
21-
packages = ["pyninja"]
21+
packages = ["pyninja", "pyninja.monitor", "pyninja.monitor.templates"]
2222
[tool.setuptools.package-data]
23-
"pyninja" = ["index.html"]
23+
"pyninja.monitor.templates" = ["*.html"]
2424

2525
[tool.setuptools.dynamic]
26-
version = {attr = "pyninja.version"}
26+
version = {attr = "pyninja.version.__version__"}
2727
dependencies = { file = ["requirements.txt"] }
2828

2929
[project.optional-dependencies]

0 commit comments

Comments
 (0)