Skip to content

Commit

Permalink
[IMPR] Provide an entry point to connect foreign scripts with pwb wapper
Browse files Browse the repository at this point in the history
- modify the wrapper script to support pywikibot entry points to support
  script packages with Pywikibot. The entry point is just the package
  path. It also checks for i18n files and sets the message package.
- define base directory of the scripts which is connected to the entry
  point.
- add pyproject.toml for the script package
- add a MANIFEST.in for the script package
- modify make_dist.py to support creating the pywikibot or the
  pywikibot-scripts distribution.
- add documentation for providing external scripts
- add sphinx-tabs
- ignore directive tabs with rstcheck as it cause warnings

Bug: T139143
Bug: T139144
Change-Id: I5705d240639190e0260299241d2bb76201dde7d2
  • Loading branch information
xqt committed Oct 3, 2024
1 parent 4afd375 commit 728062b
Show file tree
Hide file tree
Showing 12 changed files with 341 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .rstcheck.cfg
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[rstcheck]
ignore_directives=automodule,autoclass,autofunction
ignore_directives=automodule,autoclass,autofunction,tabs
ignore_messages=(Undefined substitution referenced: "(release|today|version)")
ignore_roles=api,phab,pylib,source,wiki
1 change: 1 addition & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
extensions = [
'notfound.extension',
'sphinx_copybutton',
'sphinx_tabs.tabs',
'sphinx.ext.autodoc',
'sphinx.ext.autosectionlabel',
'sphinx.ext.autosummary',
Expand Down
97 changes: 97 additions & 0 deletions docs/entrypoint.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
************************
Provide your own scripts
************************

You may provide your own scripts as a **Pywikibot** plugin. All you have
to do is to bind your package and define an entry point for pywikibot.

.. caution:: ``pywikibot >= 9.4.0`` is required for this possibility.

For example having package files like this::

my_pwb_scripts/
├── LICENSE
├── pyproject.toml
├── README.md
├── src/
│ └── wikidata_scripts/
│ ├── __init__.py
│ ├── drop_entities.py
│ └── show_entities.py
└── tests/


Add the following code in your ``wikidata_scripts.__init__.py``:

.. code-block:: python
from pathlib import Path
base_dir = Path(__file__).parent
Add *Pywikibot* dependency and register the entry point, which is the
``base_dir`` above, within your preferred config file:

.. tabs::
.. tab:: pyproject.toml

.. code-block:: toml
[project]
dependencies = [
"pywikibot >= 9.4.0",
]
[project.entry-points."pywikibot"]
scriptspath = "wikidata_scripts:base_dir"
.. tab:: setup.cfg

.. code-block:: ini
[options]
install_requires =
pywikibot >= 9.4.0
[options.entry_points]
pywikibot =
scriptspath = wikidata_scripts:base_dir
.. tab:: setup.py

.. code-block:: python
from setuptools import setup
setup(
install_requires=[
'pywikibot >= 9.4.0',
],
entry_points={
'pywikibot': [
'scriptspath = wikidata_scripts:base_dir',
]
}
)
After installing your package scripts are available via :mod:`pwb` wrapper and
can be invoked like this:

.. tabs::

.. tab:: Unix/macOS

.. code-block:: shell
$ pwb <global options> show_entities <scripts options>
.. tab:: Windows

.. code-block:: shell
pwb <global options> show_entities <scripts options>
.. note:: If you have several Pywikibot scripts installed, there script names
must be different; otherwise the started script might not that you have
expected.
.. warning:: This guide is not tested. Test it locally before uploading to pypi.
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ Contents
recipes
api_ref/index
mwapi
entrypoint

.. toctree::
:maxdepth: 1
Expand Down
1 change: 1 addition & 0 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ rstcheck >=6.2.4
sphinxext-opengraph >= 0.9.1
sphinx-copybutton >= 0.5.2
sphinx-notfound-page >= 1.0.4
sphinx-tabs >= 3.4.5
tomli; python_version < '3.11'
furo >= 2024.8.6
64 changes: 55 additions & 9 deletions make_dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
The following options are supported:
pywikibot The pywikibot repository to build (default)
scripts The pywikibot-scripts repository to build
-help Print documentation of this file and of setup.py
-local Install the distribution as a local site-package. If a
Expand All @@ -21,7 +25,7 @@
Usage::
[pwb] make_dist [options]
[pwb] make_dist [repo] [options]
.. versionadded:: 7.3
.. versionchanged:: 7.4
Expand All @@ -43,6 +47,9 @@
.. versionchanged:: 8.2
Build frontend was changed from setuptools to build. ``-upgrade``
option also installs packages if necessary.
.. versionchanged:: 9.4
The pywikibot-scripts distribution can be created.
"""
#
# (C) Pywikibot team, 2022-2024
Expand All @@ -54,6 +61,7 @@
import abc
import shutil
import sys
from contextlib import suppress
from dataclasses import dataclass, field
from importlib import import_module
from pathlib import Path
Expand All @@ -76,6 +84,8 @@ class SetupBase(abc.ABC):
remote: bool
clear: bool
upgrade: bool

build_opt: str = field(init=False)
folder: Path = field(init=False)

def __post_init__(self) -> None:
Expand All @@ -91,6 +101,8 @@ def clear_old_dist(self) -> None: # pragma: no cover
shutil.rmtree(self.folder / 'build', ignore_errors=True)
shutil.rmtree(self.folder / 'dist', ignore_errors=True)
shutil.rmtree(self.folder / 'pywikibot.egg-info', ignore_errors=True)
shutil.rmtree(self.folder / 'scripts' / 'pywikibot_scripts.egg-info',
ignore_errors=True)
info('<<lightyellow>>done')

@abc.abstractmethod
Expand Down Expand Up @@ -139,7 +151,7 @@ def build(self) -> bool: # pragma: no cover
self.copy_files()
info('<<lightyellow>>Build package')
try:
check_call('python -m build')
check_call(f'python -m build {self.build_opt}')
except Exception as e:
error(e)
return False
Expand All @@ -152,10 +164,9 @@ def build(self) -> bool: # pragma: no cover

if self.local:
info('<<lightyellow>>Install locally')
check_call('pip uninstall pywikibot -y', shell=True)
check_call(
'pip install --no-index --pre --find-links=dist pywikibot',
shell=True)
check_call(f'pip uninstall {self.package} -y', shell=True)
check_call(f'pip install --no-cache-dir --no-index --pre '
f'--find-links=dist {self.package}', shell=True)

if self.remote and input_yn(
'<<lightblue>>Upload dist to pypi', automatic_quit=False):
Expand All @@ -170,6 +181,9 @@ class SetupPywikibot(SetupBase):
.. versionadded:: 8.0
"""

build_opt = '' # defaults to current directory
package = 'pywikibot'

def __init__(self, *args) -> None:
"""Set source and target directories."""
super().__init__(*args)
Expand Down Expand Up @@ -203,6 +217,36 @@ def cleanup(self) -> None: # pragma: no cover
info('<<lightyellow>>done')


class SetupScripts(SetupBase):

"""Setup pywikibot-scripts distribution.
.. versionadded:: 9.4
"""

build_opt = '-w' # only wheel (yet)
package = 'pywikibot_scripts'
replace = 'MANIFEST.in', 'pyproject.toml', 'setup.py'

def copy_files(self) -> None:
"""Ignore copy files yet."""
info('<<lightyellow>>Copy files ...', newline=False)
for filename in self.replace:
file = self.folder / filename
file.rename(self.folder / (filename + '.saved'))
with suppress(FileNotFoundError):
shutil.copy(self.folder / 'scripts' / filename, self.folder)
info('<<lightyellow>>done')

def cleanup(self) -> None:
"""Ignore cleanup yet."""
info('<<lightyellow>>Copy files ...', newline=False)
for filename in self.replace:
file = self.folder / (filename + '.saved')
file.replace(self.folder / filename)
info('<<lightyellow>>done')


def handle_args() -> tuple[bool, bool, bool, bool]:
"""Handle arguments and print documentation if requested.
Expand All @@ -223,19 +267,21 @@ def handle_args() -> tuple[bool, bool, bool, bool]:
remote = '-remote' in sys.argv
clear = '-clear' in sys.argv
upgrade = '-upgrade' in sys.argv
scripts = 'scripts' in sys.argv

if remote and 'dev' in __version__: # pragma: no cover
warning('Distribution must not be a developmental release to upload.')
remote = False

sys.argv = [sys.argv[0]]
return local, remote, clear, upgrade
return local, remote, clear, upgrade, scripts


def main() -> None:
"""Script entry point."""
args = handle_args()
return SetupPywikibot(*args).run()
*args, scripts = handle_args()
installer = SetupScripts if scripts else SetupPywikibot
return installer(*args).run()


if __name__ == '__main__':
Expand Down
25 changes: 23 additions & 2 deletions pywikibot/scripts/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
2. User scripts residing in `scripts/userscripts` (directory mode only).
3. Scripts residing in `scripts` folder (directory mode only).
4. Maintenance scripts residing in `scripts/maintenance` (directory mode only).
5. Framework scripts residing in `pywikibot/scripts`.
5. Site-package scripts (site-package only)
6. Framework scripts residing in `pywikibot/scripts`.
This wrapper script is able to invoke scripts even if the script name is
misspelled. In directory mode it also checks package dependencies.
Expand Down Expand Up @@ -38,6 +39,8 @@
see :ref:`Environment variables`.
.. versionchanged:: 8.0
renamed to wrapper.py.
.. versionchanged:: 9.4
enable external scripts via entry points.
"""
#
# (C) Pywikibot team, 2012-2024
Expand Down Expand Up @@ -382,6 +385,8 @@ def find_filename(filename):
Search users_scripts_paths in config.base_dir
.. versionchanged:: 9.0
Add config.base_dir to search path
.. versionchanged:: 9.4
Search in entry point paths
"""
from pywikibot import config
path_list = [] # paths to find misspellings
Expand All @@ -397,6 +402,7 @@ def test_paths(paths, root: Path):
path_list.append(testpath.parent)
return None

# search through user scripts paths
user_script_paths = ['']
if config.user_script_paths: # pragma: no cover
if isinstance(config.user_script_paths, list):
Expand All @@ -410,7 +416,22 @@ def test_paths(paths, root: Path):
if found: # pragma: no cover
return found

if not site_package:
if site_package: # search for entry points
import importlib
from importlib.metadata import entry_points

from pywikibot.i18n import set_messages_package

for ep in entry_points(name='scriptspath', group='pywikibot'):
path = ep.load()
found = test_paths([''], path)
if found:
i18n_package = path.stem + '.i18n'
if importlib.import_module(i18n_package).__file__ is not None:
set_messages_package(i18n_package)
return found

else: # search in scripts folder
script_paths = [
'scripts.userscripts',
'scripts',
Expand Down
2 changes: 2 additions & 0 deletions scripts/MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
graft scripts/i18n/
include CODE_OF_CONDUCT.rst
21 changes: 9 additions & 12 deletions scripts/README.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
.. image:: https://img.shields.io/github/languages/top/wikimedia/pywikibot
:alt: Top language
:target: https://www.python.org/downloads/
.. image:: https://img.shields.io/github/last-commit/wikimedia/pywikibot
:alt: Last commit
:target: https://gerrit.wikimedia.org/r/plugins/gitiles/pywikibot/core/

###########################################################################
**This is a package to include robots for MediaWiki wikis like Wikipedia.**
###########################################################################

.. role:: api

*********************************
Some example robots are included.
*********************************
Expand Down Expand Up @@ -179,18 +188,6 @@ Maintenance
| unidata.py | Updates _first_upper_exception_dict in tools.unidata |
+------------------------+---------------------------------------------------------+

Others
======

+------------------------+---------------------------------------------------------+
| Others | |
+========================+=========================================================+
| i18n (folder) | Contains i18n translations for bot edit summaries. |
+------------------------+---------------------------------------------------------+
| userscripts (folder) | Empty folder for user scripts. |
+------------------------+---------------------------------------------------------+
| README.rst | This file (Short info of all scripts). |
+------------------------+---------------------------------------------------------+

**External packages could be required with Pywikibot:**

Expand Down
7 changes: 6 additions & 1 deletion scripts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,10 @@
#
from __future__ import annotations

from pathlib import Path

__version__ = '9.4.0'

__version__ = '9.4.0.dev1'

#: defines the entry point for pywikibot-scripts package
base_dir = Path(__file__).parent
Loading

0 comments on commit 728062b

Please sign in to comment.