jenkins-bot has submitted this change. ( 
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/861895 )

Change subject: [IMPR] Create a cookie file for each account
......................................................................

[IMPR] Create a cookie file for each account

- Add/restore PywikibotCookieJar which overrides load and save method.
  This enables lazy load of cookies during login when the username is
  known already.
- load() creates the PywikibotCookieJar.filename and tries to load
  the cookies
- save() creates the cookies file and checks the file mode
- APISite.login calls http.cookie_jar.load to load cookies for a
  given user
- remove http.SSL_CERT_VERIFY_FAILED_MSG and remove duplicate error
  message in generate_family_file.py
- add a new tools function as_filename() which ensures that a string
  can be used as filename.
- use as_filename to create a filename for the given user
- use as_filename with BasePage.title()

Bug: T324000
Change-Id: Ibb717fdb18d5bd9ed6b39e025e5a1dd1afde4eda
---
M pywikibot/tools/__init__.py
M pywikibot/comms/http.py
M pywikibot/scripts/generate_family_file.py
M pywikibot/site/_apisite.py
M pywikibot/page/_page.py
5 files changed, 129 insertions(+), 33 deletions(-)

Approvals:
  JJMC89: Looks good to me, approved
  jenkins-bot: Verified




diff --git a/pywikibot/comms/http.py b/pywikibot/comms/http.py
index 429c96b..56518b3 100644
--- a/pywikibot/comms/http.py
+++ b/pywikibot/comms/http.py
@@ -11,15 +11,19 @@
     - Basic HTTP error handling

 This module creates and uses its own ``requests.Session`` object.
-The session is closed if the module terminates.
-If required you can use your own Session object passing it to the
-``http.session`` variable::
+The session is closed if the module terminates. If required you can use
+your own Session object passing it to the ``http.session`` variable::

     from pywikibot.comms import http
     session = requests.Session()
     http.session = session

-:py:obj:`flush()` can be called to close the session object.
+To enable access via cookies, assign cookie handling class::
+
+    session.cookies = http.cookie_jar
+
+.. versionchanged:: 8.0
+   Cookies are lazy loaded when logging to site.
 """
 #
 # (C) Pywikibot team, 2007-2022
@@ -40,7 +44,7 @@
 import requests

 import pywikibot
-from pywikibot import config
+from pywikibot import config, tools
 from pywikibot.backports import Tuple
 from pywikibot.exceptions import (
     FatalServerError,
@@ -58,20 +62,48 @@
     requests_oauthlib = e


-# The error message for failed SSL certificate verification
-# 'certificate verify failed' is a commonly detectable string
-SSL_CERT_VERIFY_FAILED_MSG = 'certificate verify failed'
+class PywikibotCookieJar(cookiejar.LWPCookieJar):

-cookie_file_path = config.datafilepath('pywikibot.lwp')
-file_mode_checker(cookie_file_path, create=True)
-cookie_jar = cookiejar.LWPCookieJar(cookie_file_path)
-try:
-    cookie_jar.load(ignore_discard=True)
-except cookiejar.LoadError:
-    debug('Loading cookies failed.')
-else:
-    debug('Loaded cookies from file.')
+    """CookieJar which create the filename and checks file permissions.

+    .. versionadded:: 8.0
+    """
+
+    def load(self, user: str = '', *args, **kwargs) -> None:
+        """Loads cookies from a file.
+
+        Insert the account name to the cookie filename, set the
+        instance`s filename and load the cookies.
+
+        :param user: account name to be part of the cookie filename.
+        """
+        _user = '-' + tools.as_filename(user) if user else ''
+        self.filename = config.datafilepath(f'pywikibot{_user}.lwp')
+
+        try:
+            super().load(*args, **kwargs)
+        except (cookiejar.LoadError, FileNotFoundError):
+            debug(f'Loading cookies for user {user} failed.')
+        else:
+            debug(f'Loaded cookies for user {user} from file.')
+
+    def save(self, *args, **kwargs) -> None:
+        """Check the file mode and save cookies to a file.
+
+        .. note:: *PywikibotCookieJar* must be loaded previously to set
+           the filename.
+
+        :raises ValueError: a filename was not supplied; :meth:`load`
+            must be called first.
+        """
+        if self.filename:
+            file_mode_checker(self.filename, create=True)
+        super().save(*args, **kwargs)
+
+
+#: global :class:`PywikibotCookieJar` instance.
+cookie_jar = PywikibotCookieJar()
+#: global :class:`requests.Session`.
 session = requests.Session()
 session.cookies = cookie_jar

@@ -268,7 +300,7 @@
     """
     # TODO: do some error correcting stuff
     if isinstance(response, requests.exceptions.SSLError) \
-       and SSL_CERT_VERIFY_FAILED_MSG in str(response):
+       and 'certificate verify failed' in str(response):
         raise FatalServerError(str(response))
 
     if isinstance(response, requests.ConnectionError):
diff --git a/pywikibot/page/_page.py b/pywikibot/page/_page.py
index 1b424ed..2e9f734 100644
--- a/pywikibot/page/_page.py
+++ b/pywikibot/page/_page.py
@@ -30,7 +30,7 @@
 from warnings import warn

 import pywikibot
-from pywikibot import Timestamp, config, date, i18n, textlib
+from pywikibot import Timestamp, config, date, i18n, textlib, tools
 from pywikibot.backports import (
     Dict,
     Generator,
@@ -305,14 +305,7 @@
             encoded_title = title.encode(self.site.encoding())
             title = quote_from_bytes(encoded_title, safe='')
         if as_filename:
-            # Replace characters that are not possible in file names on some
-            # systems, but still are valid in MediaWiki titles:
-            # Unix: /
-            # MediaWiki: /:\
-            # Windows: /:\"?*
-            # Spaces are possible on most systems, but are bad for URLs.
-            for forbidden in ':*?/\\" ':
-                title = title.replace(forbidden, '_')
+            title = tools.as_filename(title)
         return title

     def section(self) -> Optional[str]:
diff --git a/pywikibot/scripts/generate_family_file.py 
b/pywikibot/scripts/generate_family_file.py
index e79a650..16e2171 100755
--- a/pywikibot/scripts/generate_family_file.py
+++ b/pywikibot/scripts/generate_family_file.py
@@ -119,8 +119,6 @@
             try:
                 w = self.Wiki(self.base_url, verify=verify)
             except FatalServerError:
-                pywikibot.error(
-                    pywikibot.comms.http.SSL_CERT_VERIFY_FAILED_MSG)
                 pywikibot.exception()
                 if not pywikibot.bot.input_yn(
                     'Retry with disabled ssl certificate validation',
diff --git a/pywikibot/site/_apisite.py b/pywikibot/site/_apisite.py
index 8032203..b6ccda2 100644
--- a/pywikibot/site/_apisite.py
+++ b/pywikibot/site/_apisite.py
@@ -25,7 +25,7 @@
     removesuffix,
 )
 from pywikibot.backports import OrderedDict as OrderedDictType
-from pywikibot.comms.http import get_authentication
+from pywikibot.comms import http
 from pywikibot.data import api
 from pywikibot.exceptions import (
     AbuseFilterDisallowedError,
@@ -326,7 +326,7 @@

     def is_oauth_token_available(self) -> bool:
         """Check whether OAuth token is set for this site."""
-        auth_token = get_authentication(self.base_url(''))
+        auth_token = http.get_authentication(self.base_url(''))
         return auth_token is not None and len(auth_token) == 4

     def login(
@@ -334,8 +334,10 @@
         autocreate: bool = False,
         user: Optional[str] = None
     ) -> None:
-        """
-        Log the user in if not already logged in.
+        """Log the user in if not already logged in.
+
+        .. versionchanged:: 8.0
+           lazy load cookies when logging in.

         .. seealso:: :api:`Login`

@@ -372,6 +374,10 @@
         self._loginstatus = _LoginStatus.IN_PROGRESS
         if user:
             self._username = normalize_username(user)
+
+        # load the password for self.username from cookie file
+        http.cookie_jar.load(self.username(), ignore_discard=True)
+
         try:
             del self.userinfo  # force reload
             if self.userinfo['name'] == self.user():
diff --git a/pywikibot/tools/__init__.py b/pywikibot/tools/__init__.py
index f59c46e..bad616f 100644
--- a/pywikibot/tools/__init__.py
+++ b/pywikibot/tools/__init__.py
@@ -60,6 +60,7 @@

     # other tools
     'PYTHON_VERSION',
+    'as_filename',
     'is_ip_address',
     'has_module',
     'classproperty',
@@ -286,6 +287,47 @@
     return (_first_upper_exception(first) or first.upper()) + string[1:]


+def as_filename(string: str, repl: str = '_') -> str:
+    r"""Return a string with characters are valid for filenames.
+
+    Replace characters that are not possible in file names on some
+    systems, but still are valid in MediaWiki titles:
+
+        - Unix: ``/``
+        - MediaWiki: ``/:\\``
+        - Windows: ``/:\\"?*``
+
+    Spaces are possible on most systems, but are bad for URLs.
+
+    **Example**:
+
+    >>> as_filename('How are you?')
+    'How_are_you_'
+    >>> as_filename('Say: "Hello"')
+    'Say___Hello_'
+    >>> as_filename('foo*bar', '')
+    'foobar'
+    >>> as_filename('foo', 'bar')
+    Traceback (most recent call last):
+    ...
+    ValueError: Invalid repl parameter 'bar'
+    >>> as_filename('foo', '?')
+    Traceback (most recent call last):
+    ...
+    ValueError: Invalid repl parameter '?'
+
+    .. versionadded:: 8.0
+
+    :param string: the string to be modified
+    :param repl: the replacement character
+    :raises ValueError: Invalid repl parameter
+    """
+    pattern = r':*?/\\" '
+    if len(repl) > 1 or len(repl) == 1 and repl in pattern:
+        raise ValueError(f'Invalid repl parameter {repl!r}')
+    return re.sub(f'[{pattern}]', repl, string)
+
+
 def strtobool(val: str) -> bool:
     """Convert a string representation of truth to True or False.


--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/861895
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.wikimedia.org/r/settings

Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Change-Id: Ibb717fdb18d5bd9ed6b39e025e5a1dd1afde4eda
Gerrit-Change-Number: 861895
Gerrit-PatchSet: 9
Gerrit-Owner: Xqt <[email protected]>
Gerrit-Reviewer: BinĂ¡ris <[email protected]>
Gerrit-Reviewer: Dalba <[email protected]>
Gerrit-Reviewer: JJMC89 <[email protected]>
Gerrit-Reviewer: jenkins-bot
Gerrit-MessageType: merged
_______________________________________________
Pywikibot-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to