Skip to content

Commit dbc4441

Browse files
committed
Add config options
1 parent 5b76633 commit dbc4441

File tree

2 files changed

+25
-15
lines changed

2 files changed

+25
-15
lines changed

pypi_browser/app.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import fluffy_code.prebuilt_styles
1010
import packaging.version
1111
import pygments.lexers.special
12+
import starlette.config
1213
from identify import identify
1314
from markupsafe import Markup
1415
from starlette.applications import Starlette
@@ -25,6 +26,7 @@
2526
import pypi_browser.packaging
2627
from pypi_browser import pypi
2728

29+
2830
PACKAGE_TYPE_NOT_SUPPORTED_ERROR = (
2931
'Sorry, this package type is not yet supported (only .zip and .whl supported currently).'
3032
)
@@ -65,6 +67,12 @@ async def dispatch(self, request, call_next):
6567
return response
6668

6769

70+
config = starlette.config.Config()
71+
pypi_config = pypi.PyPIConfig(
72+
cache_path=config('PYPI_BROWSER_PACKAGE_CACHE_PATH', default='/tmp'),
73+
pypi_url=config('PYPI_BROWSER_PYPI_URL', default='https://pypi.org'),
74+
)
75+
6876
app = Starlette(
6977
debug=os.environ.get('PYPI_BROWSER_DEBUG') == '1',
7078
middleware=[
@@ -110,7 +118,7 @@ async def package(request: Request) -> Response:
110118
return RedirectResponse(request.url_for('package', package=normalized_package_name))
111119

112120
try:
113-
version_to_files = await pypi.files_for_package(package_name)
121+
version_to_files = await pypi.files_for_package(pypi_config, package_name)
114122
except pypi.PackageDoesNotExist:
115123
return PlainTextResponse(
116124
f'Package {package_name!r} does not exist on PyPI.',
@@ -149,7 +157,7 @@ async def package_file(request: Request) -> Response:
149157
)
150158

151159
try:
152-
archive = await pypi.downloaded_file_path(package_name, file_name)
160+
archive = await pypi.downloaded_file_path(pypi_config, package_name, file_name)
153161
except pypi.PackageDoesNotExist:
154162
return PlainTextResponse(
155163
f'Package {package_name!r} does not exist on PyPI.',
@@ -223,7 +231,7 @@ async def package_file_archive_path(request: Request) -> Response:
223231
)
224232

225233
try:
226-
archive = await pypi.downloaded_file_path(package_name, file_name)
234+
archive = await pypi.downloaded_file_path(pypi_config, package_name, file_name)
227235
except pypi.PackageDoesNotExist:
228236
return PlainTextResponse(
229237
f'Package {package_name!r} does not exist on PyPI.',

pypi_browser/pypi.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import base64
22
import contextlib
3+
import dataclasses
34
import itertools
45
import os.path
56
import typing
@@ -8,26 +9,27 @@
89
import httpx
910

1011

11-
# TODO: make configurable
12-
PYPI = 'https://pypi.org'
13-
STORAGE_DIR = '/home/ckuehl/tmp/pypi-view'
12+
@dataclasses.dataclass(frozen=True)
13+
class PyPIConfig:
14+
cache_path: str
15+
pypi_url: str
1416

1517

1618
class PackageDoesNotExist(Exception):
1719
pass
1820

1921

20-
async def package_metadata(client: httpx.AsyncClient, package: str) -> typing.Dict:
21-
resp = await client.get(f'{PYPI}/pypi/{package}/json')
22+
async def package_metadata(config: PyPIConfig, client: httpx.AsyncClient, package: str) -> typing.Dict:
23+
resp = await client.get(f'{config.pypi_url}/pypi/{package}/json')
2224
if resp.status_code == 404:
2325
raise PackageDoesNotExist(package)
2426
resp.raise_for_status()
2527
return resp.json()
2628

2729

28-
async def files_for_package(package: str) -> typing.Dict[str, typing.Set[str]]:
30+
async def files_for_package(config: PyPIConfig, package: str) -> typing.Dict[str, typing.Set[str]]:
2931
async with httpx.AsyncClient() as client:
30-
metadata = await package_metadata(client, package)
32+
metadata = await package_metadata(config, client, package)
3133

3234
return {
3335
version: {file_['filename'] for file_ in files}
@@ -40,9 +42,9 @@ class CannotFindFileError(Exception):
4042
pass
4143

4244

43-
def _storage_path(package: str, filename: str) -> str:
45+
def _storage_path(config: PyPIConfig, package: str, filename: str) -> str:
4446
return os.path.join(
45-
STORAGE_DIR,
47+
config.cache_path,
4648
# Base64-encoding the names to calculate the storage path just to be
4749
# extra sure to avoid any path traversal vulnerabilities.
4850
base64.urlsafe_b64encode(package.encode('utf8')).decode('ascii'),
@@ -63,18 +65,18 @@ async def _atomic_file(path: str, mode: str = 'w') -> typing.Any:
6365
await aiofiles.os.rename(f.name, path)
6466

6567

66-
async def downloaded_file_path(package: str, filename: str) -> str:
68+
async def downloaded_file_path(config: PyPIConfig, package: str, filename: str) -> str:
6769
"""Return path on filesystem to downloaded PyPI file.
6870
6971
May be instant if the file is already cached; otherwise it will download
7072
it and may take a while.
7173
"""
72-
stored_path = _storage_path(package, filename)
74+
stored_path = _storage_path(config, package, filename)
7375
if await aiofiles.os.path.exists(stored_path):
7476
return stored_path
7577

7678
async with httpx.AsyncClient() as client:
77-
metadata = await package_metadata(client, package)
79+
metadata = await package_metadata(config, client, package)
7880

7981
# Parsing versions from non-wheel Python packages isn't perfectly
8082
# reliable, so just search through all releases until we find a

0 commit comments

Comments
 (0)