xtools 0.2.0__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,27 +1,26 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: xtools
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: XTools API wrapper
5
- Home-page: https://github.com/bfontaine/xtools
6
5
  Author: Baptiste Fontaine
7
- Author-email: b@ptistefontaine.fr
8
- Requires-Python: >=3.8,<4.0
6
+ Author-email: Baptiste Fontaine <b@ptistefontaine.fr>
9
7
  Classifier: Development Status :: 5 - Production/Stable
10
8
  Classifier: Intended Audience :: Developers
11
9
  Classifier: License :: OSI Approved :: MIT License
12
10
  Classifier: Operating System :: OS Independent
13
11
  Classifier: Programming Language :: Python :: 3
14
- Classifier: Programming Language :: Python :: 3.8
15
- Classifier: Programming Language :: Python :: 3.9
16
12
  Classifier: Programming Language :: Python :: 3.10
17
13
  Classifier: Programming Language :: Python :: 3.11
18
- Classifier: Programming Language :: Python :: 3
19
- Requires-Dist: requests (>=2.28.2,<3.0.0)
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Requires-Dist: requests>=2.28.2,<3
18
+ Requires-Python: >=3.10, <4.0
19
+ Project-URL: Homepage, https://github.com/bfontaine/xtools
20
20
  Description-Content-Type: text/markdown
21
21
 
22
22
  # xtools
23
- [![Version on Pypi](https://img.shields.io/pypi/v/xtools.svg?label=PyPI)](https://pypi.org/project/coveralls/)
24
- [![Coverage Status](https://coveralls.io/repos/github/bfontaine/xtools/badge.svg?branch=master)](https://coveralls.io/github/bfontaine/xtools?branch=master)
23
+ [![Version on Pypi](https://img.shields.io/pypi/v/xtools.svg?label=PyPI)](https://pypi.org/project/xtools/)
25
24
 
26
25
  **xtools** is a Python wrapper around [XTools][]’ API.
27
26
 
@@ -35,7 +34,7 @@ Note this project is not affiliated with nor endorsed by XTools.
35
34
 
36
35
  ### Requirements
37
36
 
38
- Python 3.8+.
37
+ Python 3.10+.
39
38
 
40
39
  ## Usage
41
40
 
@@ -1,6 +1,5 @@
1
1
  # xtools
2
- [![Version on Pypi](https://img.shields.io/pypi/v/xtools.svg?label=PyPI)](https://pypi.org/project/coveralls/)
3
- [![Coverage Status](https://coveralls.io/repos/github/bfontaine/xtools/badge.svg?branch=master)](https://coveralls.io/github/bfontaine/xtools?branch=master)
2
+ [![Version on Pypi](https://img.shields.io/pypi/v/xtools.svg?label=PyPI)](https://pypi.org/project/xtools/)
4
3
 
5
4
  **xtools** is a Python wrapper around [XTools][]’ API.
6
5
 
@@ -14,8 +13,8 @@ Note this project is not affiliated with nor endorsed by XTools.
14
13
 
15
14
  ### Requirements
16
15
 
17
- Python 3.8+.
16
+ Python 3.10+.
18
17
 
19
18
  ## Usage
20
19
 
21
- [Read the docs](https://python-xtools.readthedocs.io/en/latest/).
20
+ [Read the docs](https://python-xtools.readthedocs.io/en/latest/).
@@ -0,0 +1,68 @@
1
+ [project]
2
+ name = "xtools"
3
+ version = "0.3.0"
4
+ description = "XTools API wrapper"
5
+ authors = [{ name = "Baptiste Fontaine", email = "b@ptistefontaine.fr" }]
6
+ requires-python = ">=3.10,<4.0"
7
+ readme = "README.md"
8
+ classifiers = [
9
+ "Development Status :: 5 - Production/Stable",
10
+ "Intended Audience :: Developers",
11
+ "License :: OSI Approved :: MIT License",
12
+ "Operating System :: OS Independent",
13
+ "Programming Language :: Python :: 3",
14
+ "Programming Language :: Python :: 3.10",
15
+ "Programming Language :: Python :: 3.11",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Programming Language :: Python :: 3.13",
18
+ "Programming Language :: Python :: 3.14",
19
+ ]
20
+ dependencies = ["requests>=2.28.2,<3"]
21
+
22
+ [project.urls]
23
+ Homepage = "https://github.com/bfontaine/xtools"
24
+
25
+ [dependency-groups]
26
+ dev = [
27
+ "types-requests>=2.28.11.17,<3",
28
+ "mypy>=2,<3",
29
+ "pytest>=9,<10",
30
+ "sphinx>=8,<9",
31
+ "requests-mock>=1.10,<2",
32
+ "pytest-cov>=7,<8",
33
+ "ruff>=0.15.17",
34
+ ]
35
+
36
+ [tool.uv]
37
+ default-groups = "all"
38
+
39
+ [tool.uv.build-backend]
40
+ module-root = ""
41
+ source-include = ["xtools/py.typed"]
42
+
43
+ [build-system]
44
+ requires = ["uv_build>=0.9.17,<0.10.0"]
45
+ build-backend = "uv_build"
46
+
47
+ [tool.ruff]
48
+ line-length = 120
49
+ target-version = "py310"
50
+ [tool.ruff.lint]
51
+ select = [
52
+ "PERF",
53
+ "W",
54
+ "F",
55
+ "UP",
56
+ "ASYNC",
57
+ # "A",
58
+ # "B",
59
+ # "PT",
60
+ "C4",
61
+ "PIE800",
62
+ "SIM",
63
+ "SLOT",
64
+ "YTT",
65
+ "PLE",
66
+ "FURB",
67
+ "PLC0207",
68
+ ]
@@ -1,3 +1,4 @@
1
+ from .exceptions import BaseXToolsException, NotFound, TooManyEdits
1
2
  from .page import article_info, prose, links, top_editors, assessments
2
3
  from .project import (normalize_project, namespaces, page_assessments, page_assessments_configuration, automated_tools,
3
4
  admins_and_user_groups, admin_statistics, patroller_statistics, steward_statistics)
@@ -6,6 +7,14 @@ from .user import (simple_edit_count, number_of_pages_created, pages_created, pa
6
7
  automated_edit_counter, non_automated_edits, automated_edits, edit_summaries, top_edits,
7
8
  category_edit_counter, log_counts, namespace_totals, month_counts, time_card)
8
9
 
9
- from .exceptions import BaseXToolsException, NotFound, TooManyEdits
10
+ __version__ = "0.3.0"
10
11
 
11
- __version__ = "0.2.0"
12
+ __all__ = (
13
+ "__version__", "article_info", "prose", "links", "top_editors", "assessments", "normalize_project", "namespaces",
14
+ "page_assessments", "page_assessments_configuration", "automated_tools", "admins_and_user_groups",
15
+ "admin_statistics", "patroller_statistics", "steward_statistics", "random_quote", "single_quote", "all_quotes",
16
+ "simple_edit_count", "number_of_pages_created", "pages_created", "pages_created_iter", "automated_edit_counter",
17
+ "non_automated_edits", "automated_edits", "edit_summaries", "top_edits", "category_edit_counter", "log_counts",
18
+ "namespace_totals", "month_counts", "time_card",
19
+ "BaseXToolsException", "NotFound", "TooManyEdits",
20
+ )
@@ -1,13 +1,14 @@
1
1
  """
2
2
  Internal base utilities.
3
3
  """
4
-
5
- from typing import Optional, Tuple, Any, Sequence
4
+ import time
5
+ from collections.abc import Sequence
6
6
  from json.decoder import JSONDecodeError
7
+ from typing import Any
7
8
  from urllib.parse import quote as urlquote
8
9
 
9
- import time
10
10
  import requests
11
+
11
12
  from .exceptions import BaseXToolsException, NotFound, TooManyEdits
12
13
 
13
14
  BASE_URL = "https://xtools.wmflabs.org/api"
@@ -24,7 +25,7 @@ def url(path: str) -> str:
24
25
  return BASE_URL + path
25
26
 
26
27
 
27
- def error_exception(response: dict) -> Optional[BaseXToolsException]:
28
+ def error_exception(response: dict[str, Any]) -> BaseXToolsException | None:
28
29
  """
29
30
  Return an optional Exception instance for an error response. Return ``None`` if the given response is not an error.
30
31
  :param response: response from the API.
@@ -54,7 +55,7 @@ def error_exception(response: dict) -> Optional[BaseXToolsException]:
54
55
  return BaseXToolsException(str(error))
55
56
 
56
57
 
57
- def get(path: str, params=None, retry=3, retry_delay=1):
58
+ def get(path: str, params: dict[str, Any] | None = None, retry: int = 3, retry_delay: int = 1) -> dict[str, Any]:
58
59
  """
59
60
  Perform a GET request against the API.
60
61
  :param path:
@@ -72,7 +73,7 @@ def get(path: str, params=None, retry=3, retry_delay=1):
72
73
  r.raise_for_status()
73
74
 
74
75
  try:
75
- response = r.json()
76
+ response: dict[str, Any] = r.json()
76
77
  except JSONDecodeError as e:
77
78
  r.raise_for_status()
78
79
  raise e
@@ -83,7 +84,7 @@ def get(path: str, params=None, retry=3, retry_delay=1):
83
84
  return response
84
85
 
85
86
 
86
- def build_path(path_format: str, params: Sequence[Tuple[str, Any, str]]) -> str:
87
+ def build_path(path_format: str, params: Sequence[tuple[str, Any, str]]) -> str:
87
88
  """
88
89
  Build a path for the XTools API.
89
90
 
@@ -100,13 +101,10 @@ def build_path(path_format: str, params: Sequence[Tuple[str, Any, str]]) -> str:
100
101
  :param params:
101
102
  :return:
102
103
  """
103
- params_dict = {}
104
+ params_dict: dict[str, str] = {}
104
105
 
105
- def has_more(index):
106
- for _, val, _ in params[index + 1:]:
107
- if val:
108
- return True
109
- return False
106
+ def has_more(index: int) -> bool:
107
+ return any(val for _, val, _ in params[index + 1:])
110
108
 
111
109
  for i, (name, value, default) in enumerate(params):
112
110
  if value:
@@ -1,15 +1,15 @@
1
- from typing import Optional
1
+ from typing import Any
2
2
 
3
3
  __all__ = ['BaseXToolsException', 'NotFound', 'TooManyEdits']
4
4
 
5
5
 
6
6
  class BaseXToolsException(Exception):
7
- def __init__(self, message, code: Optional[int] = None):
7
+ def __init__(self, message: str, code: int | None = None) -> None:
8
8
  super().__init__(message)
9
9
  self.message = message
10
10
  self.code = code
11
11
 
12
- def __eq__(self, other):
12
+ def __eq__(self, other: Any) -> bool:
13
13
  return isinstance(other, BaseXToolsException) \
14
14
  and str(self) == str(other) \
15
15
  and self.code == other.code
@@ -4,13 +4,14 @@ Endpoints related to pages.
4
4
  https://xtools.readthedocs.io/en/stable/api/page.html
5
5
  """
6
6
 
7
- from typing import Optional, Sequence
7
+ from collections.abc import Sequence
8
8
  from datetime import date
9
+ from typing import Any
9
10
 
10
11
  from . import base
11
12
 
12
13
 
13
- def _get_page_dict(what: str, project: str, article: str) -> dict:
14
+ def _get_page_dict(what: str, project: str, article: str) -> dict[str, Any]:
14
15
  return base.get(base.build_path("/page/{what}/{project}/{article}", (
15
16
  ("what", what, ""),
16
17
  ("project", project, ""),
@@ -18,7 +19,7 @@ def _get_page_dict(what: str, project: str, article: str) -> dict:
18
19
  )))
19
20
 
20
21
 
21
- def article_info(project: str, article: str) -> dict:
22
+ def article_info(project: str, article: str) -> dict[str, Any]:
22
23
  """
23
24
  Return basic information about an article, such as page views, watchers, edits counts; author; assessment.
24
25
 
@@ -31,7 +32,7 @@ def article_info(project: str, article: str) -> dict:
31
32
  return _get_page_dict("articleinfo", project, article)
32
33
 
33
34
 
34
- def prose(project: str, article: str) -> dict:
35
+ def prose(project: str, article: str) -> dict[str, Any]:
35
36
  """
36
37
  Return prose information about an article, such as references and words counts.
37
38
 
@@ -58,7 +59,7 @@ def prose(project: str, article: str) -> dict:
58
59
  return _get_page_dict("prose", project, article)
59
60
 
60
61
 
61
- def links(project: str, article: str) -> dict:
62
+ def links(project: str, article: str) -> dict[str, Any]:
62
63
  """
63
64
  Return in and outgoing links counts of an article.
64
65
 
@@ -85,10 +86,10 @@ def links(project: str, article: str) -> dict:
85
86
 
86
87
 
87
88
  def top_editors(project: str, article: str,
88
- start: Optional[date] = None,
89
- end: Optional[date] = None,
90
- limit: Optional[int] = None,
91
- exclude_bots: bool = False) -> dict:
89
+ start: date | None = None,
90
+ end: date | None = None,
91
+ limit: int | None = None,
92
+ exclude_bots: bool = False) -> dict[str, Any]:
92
93
  """
93
94
  Get top editors on an article.
94
95
 
@@ -140,7 +141,7 @@ def top_editors(project: str, article: str,
140
141
 
141
142
 
142
143
  def assessments(project: str, articles: Sequence[str],
143
- class_only: bool = False) -> dict:
144
+ class_only: bool = False) -> dict[str, Any]:
144
145
  """
145
146
  Get assessment data for the given articles.
146
147
 
@@ -3,18 +3,18 @@ Endpoints related to projects.
3
3
 
4
4
  https://xtools.readthedocs.io/en/stable/api/project.html
5
5
  """
6
-
7
- from typing import Optional, Sequence
6
+ from collections.abc import Sequence
8
7
  from datetime import date
8
+ from typing import Any
9
9
 
10
10
  from . import base
11
11
 
12
12
 
13
- def _get_project_dict(what: str, project: str) -> dict:
14
- return base.get("/project/%s/%s" % (what, project))
13
+ def _get_project_dict(what: str, project: str) -> dict[str, Any]:
14
+ return base.get(f"/project/{what}/{project}")
15
15
 
16
16
 
17
- def normalize_project(project: str) -> dict:
17
+ def normalize_project(project: str) -> dict[str, Any]:
18
18
  """
19
19
  https://xtools.readthedocs.io/en/stable/api/project.html#normalize-project
20
20
 
@@ -36,7 +36,7 @@ def normalize_project(project: str) -> dict:
36
36
  return _get_project_dict("normalize", project)
37
37
 
38
38
 
39
- def namespaces(project: str) -> dict:
39
+ def namespaces(project: str) -> dict[str, Any]:
40
40
  """
41
41
  https://xtools.readthedocs.io/en/stable/api/project.html#namespaces
42
42
 
@@ -46,7 +46,7 @@ def namespaces(project: str) -> dict:
46
46
  return _get_project_dict("namespaces", project)
47
47
 
48
48
 
49
- def page_assessments(project: str) -> dict:
49
+ def page_assessments(project: str) -> dict[str, Any]:
50
50
  """
51
51
  https://xtools.readthedocs.io/en/stable/api/project.html#page-assessments
52
52
 
@@ -56,7 +56,7 @@ def page_assessments(project: str) -> dict:
56
56
  return _get_project_dict("assessments", project)
57
57
 
58
58
 
59
- def page_assessments_configuration() -> dict:
59
+ def page_assessments_configuration() -> dict[str, Any]:
60
60
  """
61
61
  Return the list of wikis that support page assessments and the configuration for each.
62
62
 
@@ -83,7 +83,7 @@ def page_assessments_configuration() -> dict:
83
83
  return base.get("/project/assessments")
84
84
 
85
85
 
86
- def automated_tools(project: str) -> dict:
86
+ def automated_tools(project: str) -> dict[str, Any]:
87
87
  """
88
88
  Return a list of known (semi-)automated tools used on the project.
89
89
 
@@ -117,7 +117,7 @@ def automated_tools(project: str) -> dict:
117
117
  return _get_project_dict("automated_tools", project)
118
118
 
119
119
 
120
- def admins_and_user_groups(project: str) -> dict:
120
+ def admins_and_user_groups(project: str) -> dict[str, Any]:
121
121
  """
122
122
  https://xtools.readthedocs.io/en/stable/api/project.html#admins-and-user-groups
123
123
 
@@ -128,9 +128,9 @@ def admins_and_user_groups(project: str) -> dict:
128
128
 
129
129
 
130
130
  def _get_project_stats_dict(what: str, project: str,
131
- start: Optional[date] = None,
132
- end: Optional[date] = None,
133
- actions: Optional[Sequence[str]] = None) -> dict:
131
+ start: date | None = None,
132
+ end: date | None = None,
133
+ actions: Sequence[str] | None = None) -> dict[str, Any]:
134
134
  path = base.build_path("/project/{what}/{project}/{start}/{end}", (
135
135
  ("what", what, ""),
136
136
  ("project", project, ""),
@@ -146,9 +146,9 @@ def _get_project_stats_dict(what: str, project: str,
146
146
 
147
147
 
148
148
  def admin_statistics(project: str,
149
- start: Optional[date] = None,
150
- end: Optional[date] = None,
151
- actions: Optional[Sequence[str]] = None) -> dict:
149
+ start: date | None = None,
150
+ end: date | None = None,
151
+ actions: Sequence[str] | None = None) -> dict[str, Any]:
152
152
  """
153
153
  Return admin users of the project with counts of the actions they took.
154
154
 
@@ -167,27 +167,27 @@ def admin_statistics(project: str,
167
167
 
168
168
 
169
169
  def patroller_statistics(project: str,
170
- start: Optional[date] = None,
171
- end: Optional[date] = None,
172
- actions: Optional[Sequence[str]] = None) -> dict:
170
+ start: date | None = None,
171
+ end: date | None = None,
172
+ actions: Sequence[str] | None = None) -> dict[str, Any]:
173
173
  """
174
174
  Same as ``admin_statistics`` with different actions.
175
175
 
176
176
  https://xtools.readthedocs.io/en/stable/api/project.html#patroller-statistics
177
177
 
178
- :param project:
179
- :param start:
180
- :param end:
178
+ :param project:
179
+ :param start:
180
+ :param end:
181
181
  :param actions: available actions include: ``patrol``, ``page-curation``, ``pc-accept``, ``pc-reject``.
182
- :return:
182
+ :return:
183
183
  """
184
184
  return _get_project_stats_dict("patroller_stats", project, start, end, actions)
185
185
 
186
186
 
187
187
  def steward_statistics(project: str,
188
- start: Optional[date] = None,
189
- end: Optional[date] = None,
190
- actions: Optional[Sequence[str]] = None) -> dict:
188
+ start: date | None = None,
189
+ end: date | None = None,
190
+ actions: Sequence[str] | None = None) -> dict[str, Any]:
191
191
  """
192
192
  Same as ``admin_statistics`` with different actions.
193
193
 
@@ -6,8 +6,9 @@ A Quote is a named tuple with ``id`` and ``text`` fields.
6
6
  https://xtools.readthedocs.io/en/stable/api/quote.html
7
7
  """
8
8
 
9
- from typing import Sequence
10
9
  from collections import namedtuple
10
+ from collections.abc import Sequence
11
+ from urllib.parse import quote as urlquote
11
12
 
12
13
  from . import base
13
14
 
@@ -44,7 +45,7 @@ def single_quote(quote_id: int) -> Quote:
44
45
  :param quote_id:
45
46
  :return: Quote.
46
47
  """
47
- return _get_quotes("/quote/%d" % quote_id)[0]
48
+ return _get_quotes(f"/quote/{urlquote(str(quote_id))}")[0]
48
49
 
49
50
 
50
51
  def all_quotes() -> Sequence[Quote]:
@@ -4,16 +4,18 @@ Endpoints related to users.
4
4
  https://xtools.readthedocs.io/en/stable/api/user.html
5
5
  """
6
6
 
7
- from typing import Optional, Generator, Sequence, Union
7
+ from collections.abc import Generator, Sequence
8
8
  from datetime import date
9
+ from typing import Any
9
10
 
10
11
  from . import base
12
+ import contextlib
11
13
 
12
14
 
13
15
  def simple_edit_count(project: str, username: str,
14
- namespace: Optional[int] = None,
15
- start: Optional[date] = None,
16
- end: Optional[date] = None) -> dict:
16
+ namespace: int | None = None,
17
+ start: date | None = None,
18
+ end: date | None = None) -> dict[str, Any]:
17
19
  """
18
20
  https://xtools.readthedocs.io/en/stable/api/user.html#simple-edit-count
19
21
 
@@ -51,11 +53,11 @@ def simple_edit_count(project: str, username: str,
51
53
 
52
54
 
53
55
  def number_of_pages_created(project: str, username: str,
54
- namespace: Optional[str] = None,
55
- redirects: Optional[str] = None,
56
- deleted: Optional[str] = None,
57
- start: Optional[date] = None,
58
- end: Optional[date] = None) -> dict:
56
+ namespace: str | None = None,
57
+ redirects: str | None = None,
58
+ deleted: str | None = None,
59
+ start: date | None = None,
60
+ end: date | None = None) -> dict[str, Any]:
59
61
  """
60
62
  Return the number of pages created by a user along with some other info.
61
63
 
@@ -99,7 +101,7 @@ def number_of_pages_created(project: str, username: str,
99
101
  return base.get(path)
100
102
 
101
103
 
102
- def _fix_page(page: dict) -> dict:
104
+ def _fix_page(page: dict[str, Any]) -> dict[str, Any]:
103
105
  """
104
106
  Fix a page dict as returned by the API.
105
107
  :param page:
@@ -113,13 +115,13 @@ def _fix_page(page: dict) -> dict:
113
115
 
114
116
 
115
117
  def pages_created(project: str, username: str,
116
- namespace: Optional[str] = None,
117
- redirects: Optional[str] = None,
118
- deleted: Optional[str] = None,
119
- start: Optional[Union[date, str]] = None,
120
- end: Optional[Union[date, str]] = None,
121
- offset: Optional[int] = None,
122
- all_times: bool = False) -> dict:
118
+ namespace: str | None = None,
119
+ redirects: str | None = None,
120
+ deleted: str | None = None,
121
+ start: date | str | None = None,
122
+ end: date | str | None = None,
123
+ offset: int | None = None,
124
+ all_times: bool = False) -> dict[str, Any]:
123
125
  """
124
126
  Return pages created by a user. This does not handle pagination; see ``pages_created_iter`` for that.
125
127
 
@@ -194,10 +196,8 @@ def pages_created(project: str, username: str,
194
196
  keys = sorted(pages.keys(), key=int)
195
197
 
196
198
  is_namespaced = False
197
- try:
199
+ with contextlib.suppress(StopIteration):
198
200
  is_namespaced = isinstance(next(pages.values().__iter__()), list)
199
- except StopIteration:
200
- pass
201
201
 
202
202
  if is_namespaced:
203
203
  ret["pages"] = [_fix_page(page) for k in keys for page in pages[k]]
@@ -208,12 +208,12 @@ def pages_created(project: str, username: str,
208
208
 
209
209
 
210
210
  def pages_created_iter(project: str, username: str,
211
- namespace: Optional[str] = None,
212
- redirects: Optional[str] = None,
213
- deleted: Optional[str] = None,
214
- start: Optional[date] = None,
215
- end: Optional[date] = None,
216
- all_times: bool = False) -> Generator[dict, None, None]:
211
+ namespace: str | None = None,
212
+ redirects: str | None = None,
213
+ deleted: str | None = None,
214
+ start: date | None = None,
215
+ end: date | None = None,
216
+ all_times: bool = False) -> Generator[dict[str, Any]]:
217
217
  """
218
218
  Equivalent of ``pages_created`` that yields page dicts and does the pagination for you.
219
219
 
@@ -235,23 +235,22 @@ def pages_created_iter(project: str, username: str,
235
235
  and ``end``.
236
236
  :return:
237
237
  """
238
- offset: Optional[int] = 0
238
+ offset: int | None = 0
239
239
 
240
240
  while offset is not None:
241
241
  ret = pages_created(project, username,
242
242
  namespace=namespace, redirects=redirects, deleted=deleted,
243
243
  start=start, end=end, all_times=all_times, offset=offset)
244
244
  offset = ret.get("continue")
245
- for page in ret["pages"]:
246
- yield page
245
+ yield from ret["pages"]
247
246
 
248
247
 
249
248
  def automated_edit_counter(project: str, username: str,
250
- namespace: Optional[str] = None,
251
- start: Optional[date] = None,
252
- end: Optional[date] = None,
253
- offset: Optional[int] = None,
254
- tools: bool = False) -> dict:
249
+ namespace: str | None = None,
250
+ start: date | None = None,
251
+ end: date | None = None,
252
+ offset: int | None = None,
253
+ tools: bool = False) -> dict[str, Any]:
255
254
  """
256
255
  https://xtools.readthedocs.io/en/stable/api/user.html#automated-edit-counter
257
256
 
@@ -278,10 +277,10 @@ def automated_edit_counter(project: str, username: str,
278
277
  return base.get(path)
279
278
 
280
279
 
281
- def _edits(what: str, project: str, username: str, namespace: Optional[str] = None,
282
- start: Optional[date] = None,
283
- end: Optional[date] = None,
284
- offset: Optional[int] = None) -> dict:
280
+ def _edits(what: str, project: str, username: str, namespace: str | None = None,
281
+ start: date | None = None,
282
+ end: date | None = None,
283
+ offset: int | None = None) -> dict[str, Any]:
285
284
  """
286
285
  Base function for both ``non_automated_edits`` and ``automated_edits``.
287
286
 
@@ -307,10 +306,10 @@ def _edits(what: str, project: str, username: str, namespace: Optional[str] = No
307
306
 
308
307
 
309
308
  def non_automated_edits(project: str, username: str,
310
- namespace: Optional[str] = None,
311
- start: Optional[date] = None,
312
- end: Optional[date] = None,
313
- offset: Optional[int] = None) -> dict:
309
+ namespace: str | None = None,
310
+ start: date | None = None,
311
+ end: date | None = None,
312
+ offset: int | None = None) -> dict[str, Any]:
314
313
  """
315
314
  https://xtools.readthedocs.io/en/stable/api/user.html#non-automated-edits
316
315
 
@@ -327,10 +326,10 @@ def non_automated_edits(project: str, username: str,
327
326
 
328
327
 
329
328
  def automated_edits(project: str, username: str,
330
- namespace: Optional[str] = None,
331
- start: Optional[date] = None,
332
- end: Optional[date] = None,
333
- offset: Optional[int] = None) -> dict:
329
+ namespace: str | None = None,
330
+ start: date | None = None,
331
+ end: date | None = None,
332
+ offset: int | None = None) -> dict[str, Any]:
334
333
  """
335
334
  https://xtools.readthedocs.io/en/stable/api/user.html#automated-edits
336
335
 
@@ -347,9 +346,9 @@ def automated_edits(project: str, username: str,
347
346
 
348
347
 
349
348
  def edit_summaries(project: str, username: str,
350
- namespace: Optional[str] = None,
351
- start: Optional[date] = None,
352
- end: Optional[date] = None) -> dict:
349
+ namespace: str | None = None,
350
+ start: date | None = None,
351
+ end: date | None = None) -> dict[str, Any]:
353
352
  """
354
353
  https://xtools.readthedocs.io/en/stable/api/user.html#edit-summaries
355
354
 
@@ -371,8 +370,8 @@ def edit_summaries(project: str, username: str,
371
370
 
372
371
 
373
372
  def top_edits(project: str, username: str,
374
- namespace: Optional[str] = None,
375
- page_title: Optional[str] = None) -> dict:
373
+ namespace: str | None = None,
374
+ page_title: str | None = None) -> dict[str, Any]:
376
375
  """
377
376
  Return the top-edited pages by a user, or all edits made by a user to a specific page.
378
377
 
@@ -395,8 +394,8 @@ def top_edits(project: str, username: str,
395
394
 
396
395
 
397
396
  def category_edit_counter(project: str, username: str, categories: Sequence[str],
398
- start: Optional[date] = None,
399
- end: Optional[date] = None) -> dict:
397
+ start: date | None = None,
398
+ end: date | None = None) -> dict[str, Any]:
400
399
  """
401
400
  https://xtools.readthedocs.io/en/stable/api/user.html#category-edit-counter
402
401
 
@@ -417,7 +416,7 @@ def category_edit_counter(project: str, username: str, categories: Sequence[str]
417
416
  return base.get(path)
418
417
 
419
418
 
420
- def log_counts(project: str, username: str) -> dict:
419
+ def log_counts(project: str, username: str) -> dict[str, Any]:
421
420
  """
422
421
  Return counts of logged actions made by a user.
423
422
 
@@ -445,10 +444,10 @@ def log_counts(project: str, username: str) -> dict:
445
444
  :param username:
446
445
  :return:
447
446
  """
448
- return base.get("/user/log_counts/%s/%s" % (project, username))
447
+ return base.get(f"/user/log_counts/{project}/{username}")
449
448
 
450
449
 
451
- def namespace_totals(project: str, username: str) -> dict:
450
+ def namespace_totals(project: str, username: str) -> dict[str, Any]:
452
451
  """
453
452
  Return the edit count for each namespace with at least 1 edit.
454
453
 
@@ -458,10 +457,10 @@ def namespace_totals(project: str, username: str) -> dict:
458
457
  :param username:
459
458
  :return:
460
459
  """
461
- return base.get("/user/namespace_totals/%s/%s" % (project, username))
460
+ return base.get(f"/user/namespace_totals/{project}/{username}")
462
461
 
463
462
 
464
- def month_counts(project: str, username: str) -> dict:
463
+ def month_counts(project: str, username: str) -> dict[str, Any]:
465
464
  """
466
465
  Return the edit count of a user, grouped by namespace then year and month.
467
466
 
@@ -471,10 +470,10 @@ def month_counts(project: str, username: str) -> dict:
471
470
  :param username:
472
471
  :return:
473
472
  """
474
- return base.get("/user/month_counts/%s/%s" % (project, username))
473
+ return base.get(f"/user/month_counts/{project}/{username}")
475
474
 
476
475
 
477
- def time_card(project: str, username: str) -> dict:
476
+ def time_card(project: str, username: str) -> dict[str, Any]:
478
477
  """
479
478
  Return the relative distribution of edits made by a user based on hour of the day and day of the week.
480
479
 
@@ -501,4 +500,4 @@ def time_card(project: str, username: str) -> dict:
501
500
  :param username:
502
501
  :return:
503
502
  """
504
- return base.get("/user/timecard/%s/%s" % (project, username))
503
+ return base.get(f"/user/timecard/{project}/{username}")
xtools-0.2.0/LICENSE DELETED
@@ -1,18 +0,0 @@
1
- Copyright © 2020-2023 – Baptiste Fontaine
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining a copy of
4
- this software and associated documentation files (the "Software"), to deal in
5
- the Software without restriction, including without limitation the rights to
6
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
- the Software, and to permit persons to whom the Software is furnished to do so,
8
- subject to the following conditions:
9
-
10
- The above copyright notice and this permission notice shall be included in all
11
- copies or substantial portions of the Software.
12
-
13
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,31 +0,0 @@
1
- [tool.poetry]
2
- name = "xtools"
3
- version = "0.2.0"
4
- description = "XTools API wrapper"
5
- authors = ["Baptiste Fontaine <b@ptistefontaine.fr>"]
6
- include = ["xtools/py.typed"]
7
- readme = "README.md"
8
- homepage = "https://github.com/bfontaine/xtools"
9
- classifiers = [
10
- "Development Status :: 5 - Production/Stable",
11
- "Intended Audience :: Developers",
12
- "License :: OSI Approved :: MIT License",
13
- "Operating System :: OS Independent",
14
- "Programming Language :: Python :: 3",
15
- ]
16
-
17
- [tool.poetry.dependencies]
18
- python = ">=3.8,<4.0"
19
- requests = "^2.28.2"
20
-
21
-
22
- [tool.poetry.group.dev.dependencies]
23
- types-requests = "^2.28.11.17"
24
- mypy = "^1"
25
- pytest = "^7.2.2"
26
- sphinx = "^6.1.3"
27
- requests-mock = "^1.10.0"
28
-
29
- [build-system]
30
- requires = ["poetry-core"]
31
- build-backend = "poetry.core.masonry.api"
File without changes