Agreguemos primero un caso de prueba para verificar el problema con múltiples cortes y autenticación.
Verifiquemos primero el archivo de prueba existente para ver dónde debemos agregar nuestra prueba.
Lectura tests/test_requests.py
Agreguemos nuestro caso de prueba al Requests de prueba clase. Primero, encontremos una buena ubicación cerca de otras pruebas relacionadas con la URL.
Buscando test_path
Perfecto, agregemos nuestro caso de prueba justo después del test_path_is_not_double_encoded prueba.
IDG
Ahora modificemos el preparar_url Método en modelos.py para preservar múltiples cortes. El problema es probable en cómo se maneja el camino antes de pasar a Urlun.
IDG
Tenga en cuenta que un nulo es una elección sospechosa del marcador.
Solucionador:
Ejecutemos las pruebas para verificar nuestros cambios solucionando el problema. Usaremos pyteste para ejecutar solo nuestro nuevo caso de prueba.
$ python -m pytest tests/test_requests.py -k test_path_with_multiple_slashes -v
============================= test session starts ==============================
platform linux -- Python 3.9.20, pytest-8.3.3, pluggy-1.5.0 -- /usr/local/bin/python
cachedir: .pytest_cache
rootdir: /repo
configfile: pyproject.toml
plugins: cov-5.0.0, httpbin-2.1.0
collecting ... collected 330 items / 329 deselected / 1 selected
tests/test_requests.py::TestRequests::test_path_with_multiple_slashes FAILED [100%]
=================================== FAILURES ===================================
_________________ TestRequests.test_path_with_multiple_slashes _________________
self =
def test_path_with_multiple_slashes(self):
"""Test that URLs with multiple slashes are preserved."""
url = "http://example.com//path//to//resource"
request = requests.Request("GET", url).prepare()
> assert request.url == url
E AssertionError: assert 'http://examp...to%00resource' == 'http://examp.../to//resource'
E
E - http://example.com//path//to//resource
E ? ^ ^^ ^^
E + http://example.com/%00path%00to%00resource
E ? ^^^ ^^^ ^^^
tests/test_requests.py:151: AssertionError
=========================== short test summary info ============================
FAILED tests/test_requests.py::TestRequests::test_path_with_multiple_slashes
====================== 1 failed, 329 deselected in 0.75s =======================
Exit Code: 1
La falla de la prueba muestra que nuestro enfoque de reemplazo de bytes nulos no funciona correctamente: los bytes nulos están codificando la URL. Intentemos un enfoque diferente utilizando un marcador temporal más seguro contra la URL.
IDG
Necesitamos agregar el re importar en la parte superior del archivo.
Verifiquemos primero las importaciones actuales.
Lectura src/requests/modelos.py
Veo que podemos agregar re a las importaciones existentes. Agregamos con las otras importaciones estándar de la biblioteca en la parte superior.
IDG
Ahora ejecutemos la prueba nuevamente para ver si nuestro nuevo enfoque funciona.
$ python -m pytest tests/test_requests.py -k test_path_with_multiple_slashes -v
============================= test session starts ==============================
platform linux -- Python 3.9.20, pytest-8.3.3, pluggy-1.5.0 -- /usr/local/bin/python
cachedir: .pytest_cache
rootdir: /repo
configfile: pyproject.toml
plugins: httpbin-2.1.0, cov-5.0.0
collecting ... collected 330 items / 329 deselected / 1 selected
tests/test_requests.py::TestRequests::test_path_with_multiple_slashes FAILED [100%]
=================================== FAILURES ===================================
_________________ TestRequests.test_path_with_multiple_slashes _________________
self =
def test_path_with_multiple_slashes(self):
"""Test that URLs with multiple slashes are preserved."""
url = "http://example.com//path//to//resource"
> request = requests.Request("GET", url).prepare()
tests/test_requests.py:150:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
src/requests/models.py:299: in prepare
p.prepare(
src/requests/models.py:368: in prepare
self.prepare_url(url, params)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = , url="http://example.com/path/to/resource"
params = {}
def prepare_url(self, url, params):
"""Prepares the given HTTP URL."""
#: Accept objects that have string representations.
#: We're unable to blindly call unicode/str functions
#: as this will include the bytestring indicator (b'')
#: on python 3.x.
#: https://github.com/psf/requests/pull/2238
if isinstance(url, bytes):
url = url.decode("utf8")
else:
url = str(url)
# Remove leading whitespaces from url
url = url.lstrip()
# Don't do any URL preparation for non-HTTP schemes like `mailto`,
# `data` etc to work around exceptions from `url_parse`, which
# handles RFC 3986 only.
if ":" in url and not url.lower().startswith("http"):
self.url = url
return
# Support for unicode domain names and paths.
try:
scheme, auth, host, port, path, query, fragment = parse_url(url)
except LocationParseError as e:
raise InvalidURL(*e.args)
if not scheme:
raise MissingSchema(
f"Invalid URL {url!r}: No scheme supplied. "
f"Perhaps you meant https://{url}?"
)
if not host:
raise InvalidURL(f"Invalid URL {url!r}: No host supplied")
# In general, we want to try IDNA encoding the hostname if the string contains
# non-ASCII characters. This allows users to automatically get the correct IDNA
# behaviour. For strings containing only ASCII characters, we need to also verify
# it doesn't start with a wildcard (*), before allowing the unencoded hostname.
if not unicode_is_ascii(host):
try:
host = self._get_idna_encoded_host(host)
except UnicodeError:
raise InvalidURL("URL has an invalid label.")
elif host.startswith(("*", ".")):
raise InvalidURL("URL has an invalid label.")
# Carefully reconstruct the network location
netloc = auth or ""
if netloc:
netloc += "@"
netloc += host
if port:
netloc += f":{port}"
# Bare domains aren't valid URLs.
if not path:
path = "/"
else:
# Preserve multiple consecutive slashes in the path
original_path = path
path = re.sub("//+", "/", path) # Normalize to single slashes for parsing
if isinstance(params, (str, bytes)):
params = to_native_string(params)
enc_params = self._encode_params(params)
if enc_params:
if query:
query = f"{query}&{enc_params}"
else:
query = enc_params
url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragment]))
# Restore multiple slashes if they were present in the original path
if path != "/" and "original_path" in locals():
# Find the path portion in the final URL and replace it
> url_parts = list(urlparse(url))
E NameError: name 'urlparse' is not defined
src/requests/models.py:490: NameError
=========================== short test summary info ============================
FAILED tests/test_requests.py::TestRequests::test_path_with_multiple_slashes
====================== 1 failed, 329 deselected in 0.55s =======================
Exit Code: 1
Ah, necesitamos usar el importado urlparse. Arreglemos las importaciones y probemos un enfoque más simple.
IDG
Ejecutemos nuevamente la prueba para ver si este enfoque más simple funciona.
La prueba pasa. La solución más simple funciona porque estamos dejando pasar los componentes de la URL sin ninguna normalización. Enviemos nuestros cambios que incluyen:
Se agregó un caso de prueba para verificar múltiples recortes se conservan en URL con y sin autenticación
Modificado preparar_url para preservar la estructura de ruta exacta sin normalización
Se agregó un comentario que explica la preservación de múltiples cortes.
Solucionador terminado
Discusión con un fundador
En algún lugar en el medio de la sesión que se muestra en la sección anterior, verá que presioné el botón Continuar solucionando y le dije a Solver: “Estás en el camino correcto; Continúe «.
Me preguntaba por qué Solucion se había detenido aquí para esperar la entrada del usuario, así que envié un informe de problema y pregunté: “¿Por qué Solucion se detiene después de identificar el problema? Sí, continuó cuando presioné el botón apropiado. Esta es una pregunta, no un informe de errores «.