Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-cloudflare for 
openSUSE:Factory checked in at 2023-10-05 20:05:03
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-cloudflare (Old)
 and      /work/SRC/openSUSE:Factory/.python-cloudflare.new.28202 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-cloudflare"

Thu Oct  5 20:05:03 2023 rev:13 rq:1115792 version:2.12.4

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-cloudflare/python-cloudflare.changes      
2023-09-06 18:58:21.987929132 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-cloudflare.new.28202/python-cloudflare.changes
   2023-10-05 20:06:17.097501612 +0200
@@ -1,0 +2,27 @@
+Thu Oct  5 09:47:35 UTC 2023 - Dirk Müller <dmuel...@suse.com>
+
+- update to 2.12.4:
+  * added ips and issue114 tests
+  * add importlib_resources info for older Python versions
+  * more api endpoints
+  * more twine tweaks
+  * Added cli4 -e option to
+    display example file path names
+  * Add `global_request_timeout` and `max_request_retries`
+    configuration options. Set default request timeout to 5s. Add
+    basic tests instantiating Cloudflare.Cloudflare
+  * more api endpoints
+  * Make RegEx string a raw string literal
+  * more api endpoints
+  * update examples and README to use == for numberic values
+  * more api endpoints
+  * remove --api option and leave --openapi in place
+  * handle quoted strings
+  * handle multipart/form-data
+    correctly for more than one file and with params/data
+  * python keywords not handled correctly at command level
+  * fix: remove future dependency and imports
+  * added after openapi review
+  * added deprecated processing, now shows version
+
+-------------------------------------------------------------------

Old:
----
  cloudflare-2.11.7.tar.gz

New:
----
  cloudflare-2.12.4.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-cloudflare.spec ++++++
--- /var/tmp/diff_new_pack.NTRkjk/_old  2023-10-05 20:06:18.201541498 +0200
+++ /var/tmp/diff_new_pack.NTRkjk/_new  2023-10-05 20:06:18.205541642 +0200
@@ -18,7 +18,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-cloudflare
-Version:        2.11.7
+Version:        2.12.4
 Release:        0
 Summary:        Python wrapper for the Cloudflare v4 API
 License:        MIT

++++++ cloudflare-2.11.7.tar.gz -> cloudflare-2.12.4.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudflare-2.11.7/CloudFlare/__init__.py 
new/cloudflare-2.12.4/CloudFlare/__init__.py
--- old/cloudflare-2.11.7/CloudFlare/__init__.py        2023-08-19 
18:23:08.000000000 +0200
+++ new/cloudflare-2.12.4/CloudFlare/__init__.py        2023-09-22 
03:45:07.000000000 +0200
@@ -1,7 +1,6 @@
 """ Cloudflare v4 API"""
-from __future__ import absolute_import
 
-__version__ = '2.11.7'
+__version__ = '2.12.4'
 
 from .cloudflare import CloudFlare
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/cloudflare-2.11.7/CloudFlare/api_decode_from_openapi.py 
new/cloudflare-2.12.4/CloudFlare/api_decode_from_openapi.py
--- old/cloudflare-2.11.7/CloudFlare/api_decode_from_openapi.py 2022-11-26 
23:35:22.000000000 +0100
+++ new/cloudflare-2.12.4/CloudFlare/api_decode_from_openapi.py 2023-09-21 
18:52:22.000000000 +0200
@@ -7,7 +7,7 @@
 
 API_TYPES = ['GET', 'POST', 'PATCH', 'PUT', 'DELETE']
 
-match_identifier = re.compile('\{[A-Za-z0-9_]*\}')
+match_identifier = re.compile(r'\{[A-Za-z0-9_]*\}')
 
 def do_path(cmd, values):
     """ do_path() """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudflare-2.11.7/CloudFlare/api_v4.py 
new/cloudflare-2.12.4/CloudFlare/api_v4.py
--- old/cloudflare-2.11.7/CloudFlare/api_v4.py  2023-08-19 18:21:25.000000000 
+0200
+++ new/cloudflare-2.12.4/CloudFlare/api_v4.py  2023-09-19 21:23:01.000000000 
+0200
@@ -145,6 +145,7 @@
     self.add('AUTH', 'zones', 'settings/development_mode')
     self.add('AUTH', 'zones', 'settings/early_hints')
     self.add('AUTH', 'zones', 'settings/email_obfuscation')
+    self.add('AUTH', 'zones', 'settings/fonts')
     self.add('AUTH', 'zones', 'settings/h2_prioritization')
     self.add('AUTH', 'zones', 'settings/hotlink_protection')
     self.add('AUTH', 'zones', 'settings/http2')
@@ -442,6 +443,11 @@
     self.add('AUTH', 'accounts', 'subscriptions')
     self.add('AUTH', 'accounts', 'tunnels')
     self.add('AUTH', 'accounts', 'tunnels', 'connections')
+
+    self.add('VOID', 'accounts', 'vectorize')
+    self.add('AUTH', 'accounts', 'vectorize/index')
+    self.add('AUTH', 'accounts', 'vectorize/indexes')
+
     self.add('AUTH', 'accounts', 'virtual_dns')
     self.add('VOID', 'accounts', 'virtual_dns', 'dns_analytics')
     self.add('AUTH', 'accounts', 'virtual_dns', 'dns_analytics/report')
@@ -466,6 +472,7 @@
     self.add('AUTH', 'accounts', 'workers/queues', 'consumers')
     self.add('AUTH', 'accounts', 'workers/scripts')
     self.add('AUTH', 'accounts', 'workers/scripts', 'content')
+    self.add('AUTH', 'accounts', 'workers/scripts', 'content/v2')
     self.add('AUTH', 'accounts', 'workers/scripts', 'schedules')
     self.add('AUTH', 'accounts', 'workers/scripts', 'settings')
     self.add('AUTH', 'accounts', 'workers/scripts', 'tails')
@@ -674,6 +681,7 @@
     self.add('AUTH', 'accounts', 'devices/unrevoke')
 
     self.add('VOID', 'accounts', 'dex')
+    self.add('AUTH', 'accounts', 'dex/colos')
     self.add('AUTH', 'accounts', 'dex/http-tests')
     self.add('AUTH', 'accounts', 'dex/tests')
     self.add('AUTH', 'accounts', 'dex/tests/unique-devices')
@@ -718,6 +726,12 @@
     self.add('AUTH', 'accounts', 'intel/domain-history')
     self.add('AUTH', 'accounts', 'intel/domain/bulk')
     self.add('AUTH', 'accounts', 'intel/indicator-feeds')
+    self.add('AUTH', 'accounts', 'intel/indicator-feeds', 'data')
+    self.add('AUTH', 'accounts', 'intel/indicator-feeds', 'snapshot')
+    self.add('VOID', 'accounts', 'intel/indicator-feeds/permissions')
+    self.add('AUTH', 'accounts', 'intel/indicator-feeds/permissions/add')
+    self.add('AUTH', 'accounts', 'intel/indicator-feeds/permissions/remove')
+    self.add('AUTH', 'accounts', 'intel/indicator-feeds/permissions/view')
     self.add('AUTH', 'accounts', 'intel/ip')
     self.add('AUTH', 'accounts', 'intel/ip-list')
     self.add('AUTH', 'accounts', 'intel/miscategorization')
@@ -738,6 +752,7 @@
     self.add('AUTH', 'accounts', 'pages/projects', 'deployments', 'retry')
     self.add('AUTH', 'accounts', 'pages/projects', 'deployments', 'rollback')
     self.add('AUTH', 'accounts', 'pages/projects', 'domains')
+    self.add('AUTH', 'accounts', 'pages/projects', 'purge_build_cache')
 
     self.add('AUTH', 'accounts', 'pcaps')
     self.add('AUTH', 'accounts', 'pcaps', 'download')
@@ -755,6 +770,10 @@
     self.add('AUTH', 'accounts', 'urlscanner/scan', 'har')
     self.add('AUTH', 'accounts', 'urlscanner/scan', 'screenshot')
 
+    self.add('VOID', 'accounts', 'hyperdrive')
+    self.add('AUTH', 'accounts', 'hyperdrive/configs')
+
+
 def zones_extras(self):
     """ zones extras """
 
@@ -787,6 +806,15 @@
     self.add('AUTH', 'zones', 'snippets', 'content')
     self.add('AUTH', 'zones', 'snippets/snippet_rules')
 
+    self.add('VOID', 'zones', 'speed_api')
+    self.add('AUTH', 'zones', 'speed_api/availabilities')
+    self.add('AUTH', 'zones', 'speed_api/pages')
+    self.add('AUTH', 'zones', 'speed_api/pages', 'tests')
+    self.add('AUTH', 'zones', 'speed_api/pages', 'trend')
+    self.add('AUTH', 'zones', 'speed_api/schedule')
+
+    self.add('VOID', 'zones', 'dcv_delegation')
+    self.add('AUTH', 'zones', 'dcv_delegation/uuid')
 
 def zones_web3(self):
     """ zones web3 """
@@ -1068,6 +1096,12 @@
     self.add('AUTH', 'radar/verified_bots/top/bots')
     self.add('AUTH', 'radar/verified_bots/top/categories')
 
+    self.add('VOID', 'radar/connection_tampering')
+    self.add('AUTH', 'radar/connection_tampering/summary')
+    self.add('AUTH', 'radar/connection_tampering/timeseries_groups')
+    self.add('AUTH', 'radar/traffic_anomalies')
+    self.add('AUTH', 'radar/traffic_anomalies/locations')
+
 def from_developers(self):
     """ from_developers """
     self.add('VOID', 'accounts', 'analytics_engine')
@@ -1119,6 +1153,7 @@
     self.add('VOID', 'accounts', 'stream/analytics')
     self.add('AUTH', 'accounts', 'stream/analytics/views')
     self.add('AUTH', 'accounts', 'stream/live_inputs', 'videos')
+    self.add('AUTH', 'accounts', 'stream/storage-usage')
 
 #   self.add('AUTH', 'organizations', 'load_balancers/monitors')
 
@@ -1157,4 +1192,3 @@
     self.add('AUTH', 'accounts', 'mtls_certificates', 'associations')
     self.add('VOID', 'accounts', 'request-tracer')
     self.add('AUTH', 'accounts', 'request-tracer/trace')
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudflare-2.11.7/CloudFlare/cloudflare.py 
new/cloudflare-2.12.4/CloudFlare/cloudflare.py
--- old/cloudflare-2.11.7/CloudFlare/cloudflare.py      2023-05-21 
19:26:25.000000000 +0200
+++ new/cloudflare-2.12.4/CloudFlare/cloudflare.py      2023-09-21 
19:03:51.000000000 +0200
@@ -1,6 +1,4 @@
 """ Cloudflare v4 API"""
-from __future__ import absolute_import
-
 import json
 import keyword
 from requests import RequestException as requests_RequestException, 
ConnectionError as requests_ConnectionError, exceptions as requests_exceptions, 
codes as requests_codes
@@ -38,8 +36,14 @@
 
             self.raw = config['raw']
             self.use_sessions = config['use_sessions']
+            self.global_request_timeout = config['global_request_timeout'] if 
'global_request_timeout' in config else None
+            self.max_request_retries = config['max_request_retries'] if 
'max_request_retries' in config else None
             self.profile = config['profile']
-            self.network = CFnetwork(use_sessions=self.use_sessions)
+            self.network = CFnetwork(
+                use_sessions=self.use_sessions,
+                global_request_timeout=self.global_request_timeout,
+                max_request_retries=self.max_request_retries
+            )
             self.user_agent = user_agent()
 
             self.logger = CFlogger(config['debug']).getLogger() if 'debug' in 
config and config['debug'] else None
@@ -909,7 +913,7 @@
 
         return api_decode_from_openapi(self._base.api_from_openapi(url))
 
-    def __init__(self, email=None, key=None, token=None, certtoken=None, 
debug=False, raw=False, use_sessions=True, profile=None, base_url=None):
+    def __init__(self, email=None, key=None, token=None, certtoken=None, 
debug=False, raw=False, use_sessions=True, profile=None, base_url=None, 
global_request_timeout=5, max_request_retries=5):
         """ Cloudflare v4 API"""
 
         self._base = None
@@ -938,6 +942,10 @@
             config['profile'] = profile
         if base_url is not None:
             config['base_url'] = base_url
+        if global_request_timeout is not None:
+            config['global_request_timeout'] = global_request_timeout
+        if max_request_retries is not None:
+            config['max_request_retries'] = max_request_retries
 
         # we do not need to handle item.call values - they pass straight thru
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudflare-2.11.7/CloudFlare/network.py 
new/cloudflare-2.12.4/CloudFlare/network.py
--- old/cloudflare-2.11.7/CloudFlare/network.py 2023-05-20 02:40:13.000000000 
+0200
+++ new/cloudflare-2.12.4/CloudFlare/network.py 2023-09-21 19:03:51.000000000 
+0200
@@ -1,52 +1,124 @@
 """ Network for Cloudflare API"""
-from __future__ import absolute_import
+
+from urllib.parse import urlparse
 
 import requests
+from requests.adapters import HTTPAdapter
 
 from .exceptions import CloudFlareAPIError
 
-class CFnetwork():
-    """ Network for Cloudflare API"""
 
-    def __init__(self, use_sessions=True):
-        """ Network for Cloudflare API"""
+class CFnetwork:
+    """Network for Cloudflare API"""
+
+    def __init__(
+        self, max_request_retries, use_sessions=True, global_request_timeout=5,
+    ):
+        """Network for Cloudflare API"""
 
         self.use_sessions = use_sessions
+        self.global_request_timeout = global_request_timeout
+        self.max_request_retries = max_request_retries
         self.session = None
 
     def __call__(self, method, url, headers=None, params=None, data=None, 
files=None):
-        """ Network for Cloudflare API"""
+        """Network for Cloudflare API"""
 
         if self.use_sessions:
             if self.session is None:
-                self.session = requests.Session()
+                s = requests.Session()
+                if self.max_request_retries is not None:
+                    hostname = urlparse(url).netloc
+                    s.mount(
+                        f"https://{hostname}";,
+                        HTTPAdapter(max_retries=self.max_request_retries),
+                    )
+                self.session = s
         else:
             self.session = requests
 
         method = method.upper()
 
         if method == 'GET':
-            r = self.session.get(url, headers=headers, params=params, 
data=data)
+            r = self.session.get(
+                url,
+                headers=headers,
+                params=params,
+                data=data,
+                timeout=self.global_request_timeout,
+            )
         elif method == 'POST':
             if isinstance(data, str):
-                r = self.session.post(url, headers=headers, params=params, 
data=data, files=files)
+                r = self.session.post(
+                    url,
+                    headers=headers,
+                    params=params,
+                    data=data,
+                    files=files,
+                    timeout=self.global_request_timeout,
+                )
             else:
-                r = self.session.post(url, headers=headers, params=params, 
json=data, files=files)
+                r = self.session.post(
+                    url,
+                    headers=headers,
+                    params=params,
+                    json=data,
+                    files=files,
+                    timeout=self.global_request_timeout,
+                )
         elif method == 'PUT':
             if isinstance(data, str):
-                r = self.session.put(url, headers=headers, params=params, 
data=data)
+                r = self.session.put(
+                    url,
+                    headers=headers,
+                    params=params,
+                    data=data,
+                    timeout=self.global_request_timeout,
+                )
             else:
-                r = self.session.put(url, headers=headers, params=params, 
json=data)
+                r = self.session.put(
+                    url,
+                    headers=headers,
+                    params=params,
+                    json=data,
+                    timeout=self.global_request_timeout,
+                )
         elif method == 'DELETE':
             if isinstance(data, str):
-                r = self.session.delete(url, headers=headers, params=params, 
data=data)
+                r = self.session.delete(
+                    url,
+                    headers=headers,
+                    params=params,
+                    data=data,
+                    timeout=self.global_request_timeout,
+                )
             else:
-                r = self.session.delete(url, headers=headers, params=params, 
json=data)
+                r = self.session.delete(
+                    url,
+                    headers=headers,
+                    params=params,
+                    json=data,
+                    timeout=self.global_request_timeout,
+                )
         elif method == 'PATCH':
             if isinstance(data, str):
-                r = self.session.request('PATCH', url, headers=headers, 
params=params, data=data)
+                r = self.session.request(
+                    'PATCH',
+                    url,
+                    headers=headers,
+                    params=params,
+                    data=data,
+                    timeout=self.global_request_timeout,
+                )
             else:
-                r = self.session.request('PATCH', url, headers=headers, 
params=params, json=data)
+                r = self.session.request(
+                    'PATCH',
+                    url,
+                    headers=headers,
+                    params=params,
+                    json=data,
+                    timeout=self.global_request_timeout,
+                )
         else:
             # should never happen
             raise CloudFlareAPIError(0, 'method not supported')
@@ -54,7 +126,7 @@
         return r
 
     def __del__(self):
-        """ Network for Cloudflare API"""
+        """Network for Cloudflare API"""
 
         if self.use_sessions and self.session:
             self.session.close()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudflare-2.11.7/CloudFlare/utils.py 
new/cloudflare-2.12.4/CloudFlare/utils.py
--- old/cloudflare-2.11.7/CloudFlare/utils.py   2023-05-20 03:00:53.000000000 
+0200
+++ new/cloudflare-2.12.4/CloudFlare/utils.py   2023-09-21 19:03:51.000000000 
+0200
@@ -1,6 +1,4 @@
 """ misc utilities  for Cloudflare API"""
-from __future__ import absolute_import
-
 import sys
 import json
 from requests import __version__ as requests__version__
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudflare-2.11.7/PKG-INFO 
new/cloudflare-2.12.4/PKG-INFO
--- old/cloudflare-2.11.7/PKG-INFO      2023-08-19 18:24:00.366338000 +0200
+++ new/cloudflare-2.12.4/PKG-INFO      2023-09-22 03:47:10.381636400 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: cloudflare
-Version: 2.11.7
+Version: 2.12.4
 Summary: Python wrapper for the Cloudflare v4 API
 Home-page: https://github.com/cloudflare/python-cloudflare
 Author: Martin J. Levy
@@ -13,6 +13,7 @@
 Classifier: License :: OSI Approved :: MIT License
 Classifier: Programming Language :: Python :: 3
 Description-Content-Type: text/markdown
+Provides-Extra: test
 License-File: LICENSE
 
 # cloudflare-python
@@ -197,7 +198,12 @@
 
 This parameter controls how the data is returned from a successful call (see 
notes below).
 
- * `raw - An optional Raw flag (True/False) - defaults to False
+ * `raw` - An optional Raw flag (True/False) - defaults to False
+
+Timeouts (10s) and Retries (5) are configured by default. Should you wish to 
override them, use these settings:
+* `global_request_timeout` - How long before each API call to Cloudflare 
should time out (in seconds)
+* `max_requests_retries` - How many times to retry an API call when DNS 
lookups, socket connections, or connect timeouts occur.
+> NOTE: `max_request_retries` is only available when `use_sessions` is not 
disabled.
 
 The following paramaters are for debug and/or development usage
 
@@ -517,6 +523,48 @@
 
 The 
[examples](https://github.com/cloudflare/python-cloudflare/tree/master/examples)
 folder contains many examples in both simple and verbose formats.
 
+You can see the installed path of these files directly via `cli4 -e` (or `cli4 
--examples`) command.
+
+```bash
+$ cli4 -e
+Python .py files:
+       ...
+       
/opt/homebrew/lib/python3.11/site-packages/examples/example_always_use_https.py
+       ...
+Bash .sh files:
+       ...
+       
/opt/homebrew/lib/python3.11/site-packages/examples/example_paging_thru_zones.sh
+       ...
+$
+```
+
+The exact path will vary depending on your system.
+The above example is MacOS and Python 3.9 hence the 
`/opt/homebrew/lib/python3.11/site-packages/` path.
+One Linux, the Python pip command may install the code is a system location 
like `/usr/lib/python3/dist-packages` or 
`~/.local/lib/python3.9/site-packages/` or something different.
+The `cli4 -e` command will try to decode the location and display the example 
files.
+
+If you are running release before Python 3.9 then you will be asked to install 
the following:
+
+```bash
+$ pip install importlib_resources
+...
+$
+```
+
+It will show up if you are running on an older system. For example, this is 
the results from running on Win7):
+
+```bash
+U:\Users\█████>cli4 -e
+Module "importlib_resources" missing - please "pip install 
importlib_resources" as your Python version is lower than 3.9
+
+U:\Users\█████>python -V
+Python 3.8.3
+
+U:\Users\█████>
+```
+
+Upgrading from an older version of Python is always recommended. Upgrading 
from Win7 is by-default even more important!
+
 ## A DNS zone code example
 
 ```python
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudflare-2.11.7/README.md 
new/cloudflare-2.12.4/README.md
--- old/cloudflare-2.11.7/README.md     2023-05-30 05:18:21.000000000 +0200
+++ new/cloudflare-2.12.4/README.md     2023-09-21 19:32:37.000000000 +0200
@@ -180,7 +180,12 @@
 
 This parameter controls how the data is returned from a successful call (see 
notes below).
 
- * `raw - An optional Raw flag (True/False) - defaults to False
+ * `raw` - An optional Raw flag (True/False) - defaults to False
+
+Timeouts (10s) and Retries (5) are configured by default. Should you wish to 
override them, use these settings:
+* `global_request_timeout` - How long before each API call to Cloudflare 
should time out (in seconds)
+* `max_requests_retries` - How many times to retry an API call when DNS 
lookups, socket connections, or connect timeouts occur.
+> NOTE: `max_request_retries` is only available when `use_sessions` is not 
disabled.
 
 The following paramaters are for debug and/or development usage
 
@@ -500,6 +505,48 @@
 
 The 
[examples](https://github.com/cloudflare/python-cloudflare/tree/master/examples)
 folder contains many examples in both simple and verbose formats.
 
+You can see the installed path of these files directly via `cli4 -e` (or `cli4 
--examples`) command.
+
+```bash
+$ cli4 -e
+Python .py files:
+       ...
+       
/opt/homebrew/lib/python3.11/site-packages/examples/example_always_use_https.py
+       ...
+Bash .sh files:
+       ...
+       
/opt/homebrew/lib/python3.11/site-packages/examples/example_paging_thru_zones.sh
+       ...
+$
+```
+
+The exact path will vary depending on your system.
+The above example is MacOS and Python 3.9 hence the 
`/opt/homebrew/lib/python3.11/site-packages/` path.
+One Linux, the Python pip command may install the code is a system location 
like `/usr/lib/python3/dist-packages` or 
`~/.local/lib/python3.9/site-packages/` or something different.
+The `cli4 -e` command will try to decode the location and display the example 
files.
+
+If you are running release before Python 3.9 then you will be asked to install 
the following:
+
+```bash
+$ pip install importlib_resources
+...
+$
+```
+
+It will show up if you are running on an older system. For example, this is 
the results from running on Win7):
+
+```bash
+U:\Users\█████>cli4 -e
+Module "importlib_resources" missing - please "pip install 
importlib_resources" as your Python version is lower than 3.9
+
+U:\Users\█████>python -V
+Python 3.8.3
+
+U:\Users\█████>
+```
+
+Upgrading from an older version of Python is always recommended. Upgrading 
from Win7 is by-default even more important!
+
 ## A DNS zone code example
 
 ```python
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudflare-2.11.7/cli4/__main__.py 
new/cloudflare-2.12.4/cli4/__main__.py
--- old/cloudflare-2.11.7/cli4/__main__.py      2020-01-15 22:29:49.000000000 
+0100
+++ new/cloudflare-2.12.4/cli4/__main__.py      2023-09-21 19:03:51.000000000 
+0200
@@ -1,7 +1,5 @@
 #!/usr/bin/env python
 """Cloudflare API via command line"""
-from __future__ import absolute_import
-
 import sys
 
 from .cli4 import cli4
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudflare-2.11.7/cli4/cli4.py 
new/cloudflare-2.12.4/cli4/cli4.py
--- old/cloudflare-2.11.7/cli4/cli4.py  2023-05-21 19:26:25.000000000 +0200
+++ new/cloudflare-2.12.4/cli4/cli4.py  2023-09-19 18:52:10.000000000 +0200
@@ -13,6 +13,7 @@
 import CloudFlare
 from .dump import dump_commands, dump_commands_from_web
 from . import converters
+from . import examples
 
 def load_and_check_yaml():
     """ load_and_check_yaml() """
@@ -365,6 +366,7 @@
 
     verbose = False
     output = 'json'
+    example = False
     raw = False
     dump = False
     openapi_url = None
@@ -374,6 +376,7 @@
 
     usage = ('usage: cli4 '
              + '[-V|--version] [-h|--help] [-v|--verbose] [-q|--quiet] '
+             + '[-e|--examples] '
              + '[-j|--json] [-y|--yaml] [-n|ndjson] '
              + '[-r|--raw] '
              + '[-d|--dump] '
@@ -386,10 +389,10 @@
 
     try:
         opts, args = getopt.getopt(args,
-                                   'VhvqjyrdA:bp:GPOUD',
+                                   'VhvqejyrdA:bp:GPOUD',
                                    [
                                        'version',
-                                       'help', 'verbose', 'quiet', 'json', 
'yaml', 'ndjson',
+                                       'help', 'verbose', 'quiet', 'examples', 
'json', 'yaml', 'ndjson',
                                        'raw',
                                        'dump', 'openapi=',
                                        'binary',
@@ -407,6 +410,8 @@
             verbose = True
         elif opt in ('-q', '--quiet'):
             output = None
+        elif opt in ('-e', '--examples'):
+            example = True
         elif opt in ('-j', '--json'):
             output = 'json'
         elif opt in ('-y', '--yaml'):
@@ -440,6 +445,13 @@
         elif opt in ('-D', '--delete'):
             method = 'DELETE'
 
+    if example:
+        try:
+            examples.display()
+        except ModuleNotFoundError as e:
+            sys.exit(e)
+        sys.exit(0)
+
     try:
         cf = CloudFlare.CloudFlare(debug=verbose, raw=raw, profile=profile)
     except Exception as e:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudflare-2.11.7/cli4/converters.py 
new/cloudflare-2.12.4/cli4/converters.py
--- old/cloudflare-2.11.7/cli4/converters.py    2022-08-24 00:30:00.000000000 
+0200
+++ new/cloudflare-2.12.4/cli4/converters.py    2023-09-21 19:03:51.000000000 
+0200
@@ -1,6 +1,4 @@
 """Cloudflare API via command line"""
-from __future__ import absolute_import
-
 import CloudFlare
 
 class ConverterError(Exception):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudflare-2.11.7/cli4/examples.py 
new/cloudflare-2.12.4/cli4/examples.py
--- old/cloudflare-2.11.7/cli4/examples.py      1970-01-01 01:00:00.000000000 
+0100
+++ new/cloudflare-2.12.4/cli4/examples.py      2023-09-19 18:52:10.000000000 
+0200
@@ -0,0 +1,39 @@
+"""Cloudflare API via command line"""
+
+import os
+import sys
+
+if sys.version_info < (3, 9):
+    # importlib.resources either doesn't exist or lacks the files()
+    # function, so use the PyPI version:
+    try:
+        import importlib_resources
+    except ModuleNotFoundError as e:
+        importlib_resources = None
+else:
+    # importlib.resources has files(), so use that:
+    import importlib.resources as importlib_resources
+
+examples_package_name = 'examples'
+
+def display():
+    """ display() """
+
+    if not importlib_resources:
+        raise ModuleNotFoundError('Module "importlib_resources" missing - 
please "pip install importlib_resources" as your Python version is lower than 
3.9')
+
+    try:
+        pkg = importlib_resources.files(examples_package_name)
+    except ModuleNotFoundError as e:
+        raise e
+
+    for ext,name in {'c': 'C', 'h': 'C', 'cc': 'C++', 'py':'Python', 
'sh':'Bash', 'awk':'AWK'}.items():
+        l = sorted(pkg.glob('**/*.' + ext))
+        if len(l) == 0:
+            continue
+        print('%s .%s files:' % (name, ext))
+        for f in sorted(pkg.glob('**/*.' + ext)):
+            if '__init__.py' in os.fspath(f):
+                continue
+            print('\t%s' % (os.fspath(f)))
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudflare-2.11.7/cloudflare.egg-info/PKG-INFO 
new/cloudflare-2.12.4/cloudflare.egg-info/PKG-INFO
--- old/cloudflare-2.11.7/cloudflare.egg-info/PKG-INFO  2023-08-19 
18:24:00.000000000 +0200
+++ new/cloudflare-2.12.4/cloudflare.egg-info/PKG-INFO  2023-09-22 
03:47:10.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: cloudflare
-Version: 2.11.7
+Version: 2.12.4
 Summary: Python wrapper for the Cloudflare v4 API
 Home-page: https://github.com/cloudflare/python-cloudflare
 Author: Martin J. Levy
@@ -13,6 +13,7 @@
 Classifier: License :: OSI Approved :: MIT License
 Classifier: Programming Language :: Python :: 3
 Description-Content-Type: text/markdown
+Provides-Extra: test
 License-File: LICENSE
 
 # cloudflare-python
@@ -197,7 +198,12 @@
 
 This parameter controls how the data is returned from a successful call (see 
notes below).
 
- * `raw - An optional Raw flag (True/False) - defaults to False
+ * `raw` - An optional Raw flag (True/False) - defaults to False
+
+Timeouts (10s) and Retries (5) are configured by default. Should you wish to 
override them, use these settings:
+* `global_request_timeout` - How long before each API call to Cloudflare 
should time out (in seconds)
+* `max_requests_retries` - How many times to retry an API call when DNS 
lookups, socket connections, or connect timeouts occur.
+> NOTE: `max_request_retries` is only available when `use_sessions` is not 
disabled.
 
 The following paramaters are for debug and/or development usage
 
@@ -517,6 +523,48 @@
 
 The 
[examples](https://github.com/cloudflare/python-cloudflare/tree/master/examples)
 folder contains many examples in both simple and verbose formats.
 
+You can see the installed path of these files directly via `cli4 -e` (or `cli4 
--examples`) command.
+
+```bash
+$ cli4 -e
+Python .py files:
+       ...
+       
/opt/homebrew/lib/python3.11/site-packages/examples/example_always_use_https.py
+       ...
+Bash .sh files:
+       ...
+       
/opt/homebrew/lib/python3.11/site-packages/examples/example_paging_thru_zones.sh
+       ...
+$
+```
+
+The exact path will vary depending on your system.
+The above example is MacOS and Python 3.9 hence the 
`/opt/homebrew/lib/python3.11/site-packages/` path.
+One Linux, the Python pip command may install the code is a system location 
like `/usr/lib/python3/dist-packages` or 
`~/.local/lib/python3.9/site-packages/` or something different.
+The `cli4 -e` command will try to decode the location and display the example 
files.
+
+If you are running release before Python 3.9 then you will be asked to install 
the following:
+
+```bash
+$ pip install importlib_resources
+...
+$
+```
+
+It will show up if you are running on an older system. For example, this is 
the results from running on Win7):
+
+```bash
+U:\Users\█████>cli4 -e
+Module "importlib_resources" missing - please "pip install 
importlib_resources" as your Python version is lower than 3.9
+
+U:\Users\█████>python -V
+Python 3.8.3
+
+U:\Users\█████>
+```
+
+Upgrading from an older version of Python is always recommended. Upgrading 
from Win7 is by-default even more important!
+
 ## A DNS zone code example
 
 ```python
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudflare-2.11.7/cloudflare.egg-info/SOURCES.txt 
new/cloudflare-2.12.4/cloudflare.egg-info/SOURCES.txt
--- old/cloudflare-2.11.7/cloudflare.egg-info/SOURCES.txt       2023-08-19 
18:24:00.000000000 +0200
+++ new/cloudflare-2.12.4/cloudflare.egg-info/SOURCES.txt       2023-09-22 
03:47:10.000000000 +0200
@@ -20,6 +20,7 @@
 cli4/cli4.py
 cli4/converters.py
 cli4/dump.py
+cli4/examples.py
 cli4/myjsonlines.py
 cli4/myyaml.py
 cloudflare.egg-info/PKG-INFO
@@ -60,4 +61,6 @@
 examples/example_with_usage.py
 examples/example_zone_search.sh
 examples/example_zones.py
-tests/test1.py
\ No newline at end of file
+tests/test_cloudflare.py
+tests/test_ips.py
+tests/test_issue114.py
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudflare-2.11.7/cloudflare.egg-info/requires.txt 
new/cloudflare-2.12.4/cloudflare.egg-info/requires.txt
--- old/cloudflare-2.11.7/cloudflare.egg-info/requires.txt      2023-08-19 
18:24:00.000000000 +0200
+++ new/cloudflare-2.12.4/cloudflare.egg-info/requires.txt      2023-09-22 
03:47:10.000000000 +0200
@@ -2,3 +2,6 @@
 pyyaml
 jsonlines
 beautifulsoup4
+
+[test]
+pytest
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudflare-2.11.7/setup.cfg 
new/cloudflare-2.12.4/setup.cfg
--- old/cloudflare-2.11.7/setup.cfg     2023-08-19 18:24:00.366533500 +0200
+++ new/cloudflare-2.12.4/setup.cfg     2023-09-22 03:47:10.381838600 +0200
@@ -1,3 +1,11 @@
+[options.extras_require]
+test = 
+       pytest
+
+[tool:pytest]
+testpaths = 
+       tests
+
 [egg_info]
 tag_build = 
 tag_date = 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudflare-2.11.7/tests/test1.py 
new/cloudflare-2.12.4/tests/test1.py
--- old/cloudflare-2.11.7/tests/test1.py        2020-01-26 21:23:53.000000000 
+0100
+++ new/cloudflare-2.12.4/tests/test1.py        1970-01-01 01:00:00.000000000 
+0100
@@ -1,16 +0,0 @@
-#!/usr/bin/env python
-
-import os
-import sys
-sys.path.insert(0, os.path.abspath('..'))
-import CloudFlare
-
-import pytest
-
-def test_ips():
-    cf = CloudFlare.CloudFlare()
-    ips = cf.ips.get()
-    assert ips
-
-if __name__ == '__main__':
-    pytest.main([__file__])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudflare-2.11.7/tests/test_cloudflare.py 
new/cloudflare-2.12.4/tests/test_cloudflare.py
--- old/cloudflare-2.11.7/tests/test_cloudflare.py      1970-01-01 
01:00:00.000000000 +0100
+++ new/cloudflare-2.12.4/tests/test_cloudflare.py      2023-09-19 
21:12:22.000000000 +0200
@@ -0,0 +1,19 @@
+import os
+import sys
+sys.path.insert(0, os.path.abspath('..'))
+
+import CloudFlare
+
+class TestCloudflare:
+    def test_creating_default_client(self):
+        cf = CloudFlare.CloudFlare()
+        assert isinstance(cf, CloudFlare.CloudFlare)
+
+
+    def test_with_global_request_timeout(self):
+        cf = CloudFlare.CloudFlare({'global_request_timeout': 10})
+        assert isinstance(cf, CloudFlare.CloudFlare)
+
+    def test_with_max_request_retries(self):
+        cf = CloudFlare.CloudFlare({'max_request_retries': 2})
+        assert isinstance(cf, CloudFlare.CloudFlare)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudflare-2.11.7/tests/test_ips.py 
new/cloudflare-2.12.4/tests/test_ips.py
--- old/cloudflare-2.11.7/tests/test_ips.py     1970-01-01 01:00:00.000000000 
+0100
+++ new/cloudflare-2.12.4/tests/test_ips.py     2023-09-21 20:33:41.000000000 
+0200
@@ -0,0 +1,20 @@
+""" ips tests """
+
+import os
+import sys
+sys.path.insert(0, os.path.abspath('..'))
+
+import CloudFlare
+
+class TestCloudflare:
+    def test_ips(self):
+        # no auth required
+        cf = CloudFlare.CloudFlare()
+        assert isinstance(cf, CloudFlare.CloudFlare)
+        ips = cf.ips()
+        assert isinstance(ips, dict)
+        assert isinstance(ips['ipv4_cidrs'], list)
+        assert isinstance(ips['ipv6_cidrs'], list)
+        assert len(ips['ipv4_cidrs']) > 0
+        assert len(ips['ipv6_cidrs']) > 0
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudflare-2.11.7/tests/test_issue114.py 
new/cloudflare-2.12.4/tests/test_issue114.py
--- old/cloudflare-2.11.7/tests/test_issue114.py        1970-01-01 
01:00:00.000000000 +0100
+++ new/cloudflare-2.12.4/tests/test_issue114.py        2023-09-21 
21:15:42.000000000 +0200
@@ -0,0 +1,127 @@
+""" issue-114 tests """
+
+import os
+import sys
+sys.path.insert(0, os.path.abspath('..'))
+
+import CloudFlare
+
+# CloudFlare(email=None, key=None, token=None, certtoken=None, debug=False, 
raw=False, use_sessions=True, profile=None, base_url=None, 
global_request_timeout=5, max_request_retries=5)
+
+class TestCloudflare:
+    """ TestCloudflare """
+    def test_email_key_token(self):
+        """ test_email_key_token """
+        # Always clear environment
+        self._setup()
+
+        profile = self._profile
+
+        assert self._email or self._key or self._token
+
+        # if not self._email and not self._key and not self._token:
+        #     assert 'EMAIL/KEY/TOKEN all needed in order to run this test' == 
''
+
+        cf = None
+        # loop over each combination
+        for email in [None, self._email, 'exam...@example.com']:
+            for key in [None, self._key, self._token]:
+                for token in [None, self._token, self._key]:
+                    print('email = ', self._obfuscate(email), 'key = ', 
self._obfuscate(key), 'token = ', self._obfuscate(token))
+                    if cf:
+                        del cf
+                    cf = CloudFlare.CloudFlare(email=email, key=key, 
token=token, profile=profile)
+                    assert isinstance(cf, CloudFlare.CloudFlare)
+
+                    try:
+                        r = cf.zones(params={'per_page':1})
+                    except:
+                        r = None
+
+                    if email is None and key is None and token == self._token:
+                        assert isinstance(r, list)
+                        assert len(r) == 1
+                        assert isinstance(r[0], dict)
+                        continue
+
+                    if email is None and key == self._token and token is None:
+                        assert isinstance(r, list)
+                        assert len(r) == 1
+                        assert isinstance(r[0], dict)
+                        continue
+
+                    if email == self._email and key == self._key and token is 
None:
+                        assert isinstance(r, list)
+                        assert len(r) == 1
+                        assert isinstance(r[0], dict)
+                        continue
+
+                    if email == self._email and key is None and token == 
self._key:
+                        assert isinstance(r, list)
+                        assert len(r) == 1
+                        assert isinstance(r[0], dict)
+                        continue
+
+                    # Nothing else should work!
+                    assert r is None
+
+    def _setup(self):
+        """ setup """
+        # Force no profile to be picked
+        self._profile=''
+        # read in email/key/token from config file(s)
+        _config_files = [
+            '.cloudflare.cfg',
+            os.path.expanduser('~/.cloudflare.cfg'),
+            os.path.expanduser('~/.cloudflare/cloudflare.cfg')
+        ]
+        email = None
+        key = None
+        token = None
+        for filename in _config_files:
+            try:
+                with open(filename, 'r') as fd:
+                    for l in fd:
+                        if email and key and token:
+                            break
+                        if l[0] == '#':
+                            continue
+                        a = l.split()
+                        if len(a) < 3:
+                            continue
+                        if a[1] != '=':
+                            continue
+                        if not email and a[0] == 'email':
+                            email = a[2]
+                            continue
+                        if not key and a[0] == 'key':
+                            key = a[2]
+                            continue
+                        if not token and a[0] == 'token':
+                            token = a[2]
+                            continue
+                break
+            except FileNotFoundError:
+                pass
+        self._email = email
+        self._key = key
+        self._token = token
+
+        # now remove all env variables!
+        for env in ['CLOUDFLARE_EMAIL', 'CLOUDFLARE_API_KEY', 
'CLOUDFLARE_API_TOKEN']:
+            try:
+                del os.environ[env]
+            except KeyError:
+                pass
+        for env in ['CF_API_EMAIL', 'CF_API_KEY', 'CF_API_TOKEN']:
+            try:
+                del os.environ[env]
+            except KeyError:
+                pass
+
+    def _obfuscate(self, s):
+        """ _obfuscate """
+
+        if s is None:
+            return ''
+        return '█' * len(s)

Reply via email to