1
0
mirror of https://github.com/pypiserver/pypiserver synced 2024-12-20 13:55:49 +01:00

bump bottle to version 0.14-dev (#619)

* bump bottle to version 0.14-dev

Support modern python versions.

This is commit 7af7135965e6e02993fea89d8932a4bc713b6692
from bottle's master branch.

Signed-off-by: Oz Tiram <oz.tiram@gmail.com>

* chore: update declared python versions

Drop everything older than 3.9.

* chore: clean old python versions from tox

Add python3.12 to tox

* fix: remove assigning custom_full path to request

Instead use a function call. This less expensive than
before_request hook (which is invoked to every route).

Signed-off-by: Oz Tiram <oz.tiram@gmail.com>

* fix: correct server adapters name

match those classes found in bottle-0.14-dev

* chore: remove unused custom_host

Signed-off-by: Oz Tiram <oz.tiram@gmail.com>

* fix: redirectio to bad_url_path

moved get_bad_url_redirect_path from core.py to _app.py

Signed-off-by: Oz Tiram <oz.tiram@gmail.com>

* fix: tests for core and _app after moving stuff around

* fix: properly create a Request object for test_redirect_project_encodes_newlines

* chore: emove unused core import

* fix: black formatting

* fix: remove constraint on python 3.12

This was probably relevant because of bottle not supporting this
Python version.

---------

Signed-off-by: Oz Tiram <oz.tiram@gmail.com>
This commit is contained in:
Oz Tiram 2024-11-08 17:51:39 +01:00 committed by GitHub
parent 16c9cecdd3
commit e6926d56a5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 2033 additions and 1139 deletions

@ -69,9 +69,10 @@ class AutoServer(enum.Enum):
"""Expected servers that can be automaticlaly selected by bottle."""
Waitress = enum.auto()
Paste = enum.auto()
PasteServer = enum.auto()
Twisted = enum.auto()
CherryPy = enum.auto()
Cheroot = enum.auto()
WsgiRef = enum.auto()
@ -79,10 +80,10 @@ class AutoServer(enum.Enum):
# auto servers in bottle.py
AUTO_SERVER_IMPORTS = (
(AutoServer.Waitress, "waitress"),
(AutoServer.Paste, "paste"),
(AutoServer.PasteServer, "paste"),
(AutoServer.Twisted, "twisted.web"),
(AutoServer.CherryPy, "cheroot.wsgi"),
(AutoServer.CherryPy, "cherrypy.wsgiserver"),
(AutoServer.CherryPy, "cherrypy"),
(AutoServer.Cheroot, "cheroot"),
# this should always be available because it's part of the stdlib
(AutoServer.WsgiRef, "wsgiref"),
)

@ -9,11 +9,10 @@ from collections import defaultdict
from collections import namedtuple
from io import BytesIO
from json import dumps
from urllib.parse import urljoin, urlparse
from urllib.parse import urljoin, urlparse, quote
from pypiserver.config import RunConfig
from . import __version__
from . import core
from .bottle import (
static_file,
redirect,
@ -30,6 +29,22 @@ config: RunConfig
app = Bottle()
def request_fullpath(request):
parsed = urlparse(request.urlparts.scheme + "://" + request.urlparts.netloc)
return parsed.path.rstrip("/") + "/" + request.fullpath.lstrip("/")
def get_bad_url_redirect_path(request, project):
"""Get the path for a bad root url."""
uri = request_fullpath(request)
if uri.endswith("/"):
uri = uri[:-1]
uri = uri.rsplit("/", 1)[0]
project = quote(project)
uri += f"/simple/{project}/"
return uri
class auth:
"""decorator to apply authentication if specified for the decorated method & action"""
@ -55,15 +70,6 @@ def log_request():
log.info(config.log_req_frmt, request.environ)
@app.hook("before_request")
def print_request():
parsed = urlparse(request.urlparts.scheme + "://" + request.urlparts.netloc)
request.custom_host = parsed.netloc
request.custom_fullpath = (
parsed.path.rstrip("/") + "/" + request.fullpath.lstrip("/")
)
@app.hook("after_request")
def log_response():
log.info(
@ -90,7 +96,8 @@ def favicon():
@app.route("/")
def root():
fp = request.custom_fullpath
parsed = urlparse(request.urlparts.scheme + "://" + request.urlparts.netloc)
fp = parsed.path.rstrip("/") + "/" + request.fullpath.lstrip("/")
# Ensure template() does not consider `msg` as filename!
msg = config.welcome_msg + "\n"
@ -212,7 +219,7 @@ def update():
@app.route("/packages")
@auth("list")
def pep_503_redirects(project=None):
return redirect(request.custom_fullpath + "/", 301)
return redirect(request_fullpath(request) + "/", 301)
@app.post("/RPC2")
@ -291,7 +298,7 @@ def simple(project):
return redirect(f"{config.fallback_url.rstrip('/')}/{project}/")
return HTTPError(404, f"Not Found ({normalized} does not exist)\n\n")
current_uri = request.custom_fullpath
current_uri = request_fullpath(request)
links = (
(
@ -322,7 +329,7 @@ def simple(project):
@app.route("/packages/")
@auth("list")
def list_packages():
fp = request.custom_fullpath
fp = request_fullpath(request)
packages = sorted(
config.backend.get_all_packages(),
key=lambda x: (os.path.dirname(x.relfn), x.pkgname, x.parsed_version),
@ -405,4 +412,4 @@ def json_info(project):
@app.route("/:project/")
def bad_url(project):
"""Redirect unknown root URLs to /simple/."""
return redirect(core.get_bad_url_redirect_path(request, project))
return redirect(get_bad_url_redirect_path(request, project))

File diff suppressed because it is too large Load Diff

@ -3,7 +3,6 @@
import mimetypes
import typing as t
from urllib.parse import quote
from pypiserver.pkg_helpers import normalize_pkgname, parse_version
@ -12,17 +11,6 @@ mimetypes.add_type("application/octet-stream", ".whl")
mimetypes.add_type("text/plain", ".asc")
def get_bad_url_redirect_path(request, project):
"""Get the path for a bad root url."""
uri = request.custom_fullpath
if uri.endswith("/"):
uri = uri[:-1]
uri = uri.rsplit("/", 1)[0]
project = quote(project)
uri += f"/simple/{project}/"
return uri
class PkgFile:
__slots__ = [
"pkgname", # The projects/package name with possible capitalization

@ -22,7 +22,7 @@ setup_requires = [
install_requires = [
"pip>=7",
"packaging>=23.2",
"importlib_resources;python_version>'3.8' and python_version<'3.12'",
"importlib_resources;python_version>'3.8'",
]
@ -77,11 +77,10 @@ setup(
"Operating System :: POSIX",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Software Development :: Build Tools",

@ -13,7 +13,7 @@ import webtest
# Local Imports
from tests.test_pkg_helpers import files, invalid_files
from pypiserver import __main__, bottle, core, Bottle
from pypiserver import __main__, bottle, core, Bottle, _app
from pypiserver.backend import CachingFileBackend, SimpleFileBackend
# Enable logging to detect any problems with it
@ -104,9 +104,14 @@ def welcome_file_all_vars(request, root):
def add_file_to_root(app):
def file_adder(root, filename, content=""):
root.join(filename).write(content)
backend = app.config.backend
if isinstance(backend, CachingFileBackend):
backend.cache_manager.invalidate_root_cache(root)
# bottle has deprecated the use of attribute access
# in configdicts
try:
backend = app.config.backend
if isinstance(backend, CachingFileBackend):
backend.cache_manager.invalidate_root_cache(root)
except AttributeError:
pass
return file_adder
@ -722,3 +727,13 @@ class TestRemovePkg:
)
assert resp.status == "404 Not Found"
assert "foo (123) not found" in unescape(resp.text)
def test_redirect_project_encodes_newlines():
"""Ensure raw newlines are url encoded in the generated redirect."""
project = "\nSet-Cookie:malicious=1;"
request = bottle.Request(
{"HTTP_X_FORWARDED_PROTO": "/\nSet-Cookie:malicious=1;"}
)
newpath = _app.get_bad_url_redirect_path(request, project)
assert "\n" not in newpath

@ -59,14 +59,6 @@ def test_fname_and_hash(tmp_path, hash_algo):
assert pkgfile.fname_and_hash == f"{f.name}#{digester(pkgfile)}"
def test_redirect_project_encodes_newlines():
"""Ensure raw newlines are url encoded in the generated redirect."""
request = Namespace(custom_fullpath="/\nSet-Cookie:malicious=1;")
project = "\nSet-Cookie:malicious=1;"
newpath = core.get_bad_url_redirect_path(request, project)
assert "\n" not in newpath
def test_normalize_pkgname_for_url_encodes_newlines():
"""Ensure newlines are url encoded in package names for urls."""
assert "\n" not in normalize_pkgname_for_url("/\nSet-Cookie:malicious=1;")

@ -1,5 +1,5 @@
[tox]
envlist = py36, py37, py38, py39, py310, py311, pypy3
envlist = py310, py311, py312, pypy3
[testenv]
deps=-r{toxinidir}/requirements/test.pip