]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
change verbiage stating exact compliance with RFC-1738
authorMike Bayer <mike_mp@zzzcomputing.com>
Sat, 17 Sep 2022 14:33:55 +0000 (10:33 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sat, 17 Sep 2022 14:41:48 +0000 (10:41 -0400)
As long as we aren't using urlparse() to parse URLs,
we are not RFC-1738 compliant.   As we accept underscores
in the scheme and not dashes or dots, we are not
RFC-1738 compliant, so emulate language like
that of PostgreSQL [1] that we "generally follow" this
scheme but include some exceptions.

[1] https://www.postgresql.org/docs/current/libpq-connect.html#id-1.7.3.8.3.6
Fixes: #8519
Change-Id: I2d7e55d9df17aed122cebb2c4c315f56c06a3da5

doc/build/changelog/changelog_02.rst
doc/build/changelog/changelog_09.rst
doc/build/core/engines.rst
lib/sqlalchemy/engine/url.py

index 69805d609803fda53978808d4ce83dc6470ee428..3d40a79a32a0b5877db75a709f590dfe05ab59ca 100644 (file)
         :tickets:
 
       create_engine now takes only RFC-1738-style strings:
-      driver://user:password@host:port/database
+      ``driver://user:password@host:port/database``
+
+      **update** this format is generally but not exactly RFC-1738,
+      including that underscores, not dashes or periods, are accepted in the
+      "scheme" portion.
 
     .. change::
         :tags:
index acf1ede92322c0726533db0ef64e57b9de5c1f35..c9ec5f3a49ac1898b24988effd4abc2647f7a65c 100644 (file)
         :tags: bug, engine
         :tickets: 2873
 
-        The :func:`_sa.create_engine` routine and the related
-        :func:`.make_url` function no longer considers the ``+`` sign
-        to be a space within the password field.  The parsing has been
-        adjusted to match RFC 1738 exactly, in that both ``username``
-        and ``password`` expect only ``:``, ``@``, and ``/`` to be
+        The :func:`_sa.create_engine` routine and the related :func:`.make_url`
+        function no longer considers the ``+`` sign to be a space within the
+        password field. The parsing in this area has been adjusted to match
+        more closely to how RFC 1738 handles these tokens, in that both
+        ``username`` and ``password`` expect only ``:``, ``@``, and ``/`` to be
         encoded.
 
         .. seealso::
index 91f6b1cabf90dc9eaa531e56d336604c7e77ae79..60895ba966349dc97d71d9e7ceee919ca2930e52 100644 (file)
@@ -55,12 +55,14 @@ See the section :ref:`dialect_toplevel` for information on the various backends
 Database URLs
 =============
 
-The :func:`_sa.create_engine` function produces an :class:`_engine.Engine` object based
-on a URL.  These URLs follow `RFC-1738
-<https://www.ietf.org/rfc/rfc1738.txt>`_, and usually can include username, password,
-hostname, database name as well as optional keyword arguments for additional configuration.
-In some cases a file path is accepted, and in others a "data source name" replaces
-the "host" and "database" portions.  The typical form of a database URL is:
+The :func:`_sa.create_engine` function produces an :class:`_engine.Engine`
+object based on a URL. The format of the URL generally follows `RFC-1738
+<https://www.ietf.org/rfc/rfc1738.txt>`_, with some exceptions, including that
+underscores, not dashes or periods, are accepted within the "scheme" portion.
+URLs typically include username, password, hostname, database name fields, as
+well as optional keyword arguments for additional configuration. In some cases
+a file path is accepted, and in others a "data source name" replaces the "host"
+and "database" portions. The typical form of a database URL is:
 
 .. sourcecode:: none
 
index 6dea3677e972cdc9d02b943b412c9a90891537c3..8d80cfd1c8770c73836ecf5bc8dd6bb858b4f880 100644 (file)
@@ -47,9 +47,10 @@ class URL(NamedTuple):
     Represent the components of a URL used to connect to a database.
 
     This object is suitable to be passed directly to a
-    :func:`_sa.create_engine` call.  The fields of the URL are parsed
-    from a string by the :func:`.make_url` function.  The string
-    format of the URL is an RFC-1738-style string.
+    :func:`_sa.create_engine` call. The fields of the URL are parsed from a
+    string by the :func:`.make_url` function. The string format of the URL
+    generally follows `RFC-1738 <https://www.ietf.org/rfc/rfc1738.txt>`_, with
+    some exceptions.
 
     To create a new :class:`_engine.URL` object, use the
     :func:`.make_url` function.  To construct a :class:`_engine.URL`
@@ -614,12 +615,12 @@ class URL(NamedTuple):
         """
         s = self.drivername + "://"
         if self.username is not None:
-            s += _rfc_1738_quote(self.username)
+            s += _sqla_url_quote(self.username)
             if self.password is not None:
                 s += ":" + (
                     "***"
                     if hide_password
-                    else _rfc_1738_quote(str(self.password))
+                    else _sqla_url_quote(str(self.password))
                 )
             s += "@"
         if self.host is not None:
@@ -817,8 +818,12 @@ class URL(NamedTuple):
 def make_url(name_or_url: Union[str, URL]) -> URL:
     """Given a string, produce a new URL instance.
 
-    The given string is parsed according to the RFC 1738 spec.  If an
-    existing URL object is passed, just returns the object.
+    The format of the URL generally follows `RFC-1738
+    <https://www.ietf.org/rfc/rfc1738.txt>`_, with some exceptions, including
+    that underscores, and not dashes or periods, are accepted within the
+    "scheme" portion.
+
+    If a :class:`.URL` object is passed, it is returned as is.
 
     .. seealso::
 
@@ -827,12 +832,12 @@ def make_url(name_or_url: Union[str, URL]) -> URL:
     """
 
     if isinstance(name_or_url, str):
-        return _parse_rfc1738_args(name_or_url)
+        return _parse_url(name_or_url)
     else:
         return name_or_url
 
 
-def _parse_rfc1738_args(name: str) -> URL:
+def _parse_url(name: str) -> URL:
     pattern = re.compile(
         r"""
             (?P<name>[\w\+]+)://
@@ -871,10 +876,10 @@ def _parse_rfc1738_args(name: str) -> URL:
         components["query"] = query
 
         if components["username"] is not None:
-            components["username"] = _rfc_1738_unquote(components["username"])
+            components["username"] = _sqla_url_unquote(components["username"])
 
         if components["password"] is not None:
-            components["password"] = _rfc_1738_unquote(components["password"])
+            components["password"] = _sqla_url_unquote(components["password"])
 
         ipv4host = components.pop("ipv4host")
         ipv6host = components.pop("ipv6host")
@@ -888,12 +893,12 @@ def _parse_rfc1738_args(name: str) -> URL:
 
     else:
         raise exc.ArgumentError(
-            "Could not parse rfc1738 URL from string '%s'" % name
+            "Could not parse SQLAlchemy URL from string '%s'" % name
         )
 
 
-def _rfc_1738_quote(text: str) -> str:
+def _sqla_url_quote(text: str) -> str:
     return re.sub(r"[:@/]", lambda m: "%%%X" % ord(m.group(0)), text)
 
 
-_rfc_1738_unquote = unquote
+_sqla_url_unquote = unquote