jenkins-bot has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/core/+/842929 )
Change subject: [FIX] Enable site-package installation from git repository (Part 2) ......................................................................
[FIX] Enable site-package installation from git repository (Part 2)
- Check the Python version in pwb.py caller script and remove this check from setup.py because site-package has its own validation. - give up Python 2.7 compatibility of the wrapper.py script. use pathlib to extract the root direcories. Also remove exception handling which is no longer needed. - update make_dist.py - add pwb to documentation
Bug: T320851 Change-Id: Id9d7344e740bb848a4b0972d4f3b2362634dc1ba --- D call_pwb.py M docs/utilities/install.rst M make_dist.py A pwb.py M pywikibot/scripts/wrapper.py M setup.py 6 files changed, 186 insertions(+), 160 deletions(-)
Approvals: Xqt: Looks good to me, approved jenkins-bot: Verified
diff --git a/call_pwb.py b/call_pwb.py deleted file mode 100644 index a5d7764..0000000 --- a/call_pwb.py +++ /dev/null @@ -1,15 +0,0 @@ -"""pwb caller script to invoke the :mod:`pywikibot.scripts.wrapper` script. - -.. versionadded:: 8.0 -""" -# -# (C) Pywikibot team, 2022 -# -# Distributed under the terms of the MIT license. -# -import sys - -from pywikibot.scripts.wrapper import main - -if __name__ == '__main__': - sys.exit(main()) diff --git a/docs/utilities/install.rst b/docs/utilities/install.rst index 475109e..dbf4134 100644 --- a/docs/utilities/install.rst +++ b/docs/utilities/install.rst @@ -10,3 +10,8 @@ ------------
.. automodule:: setup + +entry point +----------- + +.. automodule:: pwb diff --git a/make_dist.py b/make_dist.py index e91cd40..053ddae 100644 --- a/make_dist.py +++ b/make_dist.py @@ -3,21 +3,21 @@
The following options are supported:
--help Print documentation of this file and of setup.py +-help Print documentation of this file and of setup.py
--local Install the distribution as a local site-package. If a - Pywikibot package is already there, it will be uninstalled - first. +-local Install the distribution as a local site-package. If a + Pywikibot package is already there, it will be uninstalled + first.
--remote Upload the package to pypi. This cannot be done if the - Pywikibot version is a development release. +-remote Upload the package to pypi. This cannot be done if the + Pywikibot version is a development release.
--clear Clear old dist folders +-clear Clear old dist folders
--upgrade Upgrade distribution packages pip, setuptools, wheel and twine - first +-upgrade Upgrade distribution packages pip, setuptools, wheel and twine + first
--nodist Do not create a distribution. Useful to -clear or -upgrade only. +-nodist Do not create a distribution. Useful to -clear or -upgrade only.
Usage::
@@ -41,6 +41,7 @@ # # Distributed under the terms of the MIT license. # +import abc import shutil import sys from subprocess import check_call @@ -51,56 +52,111 @@ import setup
-def clear_old_dist() -> None: # pragma: no cover - """Delete old dist folders. +class SetupBase(abc.ABC):
- .. versionadded:: 7.5 + """Setup distribution base class. + + .. versionadded:: 8.0 """ - info('Removing old dist folders... ', newline=False) - folder = Path().resolve() - shutil.rmtree(folder / 'build', ignore_errors=True) - shutil.rmtree(folder / 'dist', ignore_errors=True) - shutil.rmtree(folder / 'pywikibot.egg-info', ignore_errors=True) - info('done') + + def __init__(self, local, remote, clear, upgrade, nodist) -> None: + """Initializer.""" + self.local = local + self.remote = remote + self.clear = clear + self.upgrade = upgrade + self.nodist = nodist + self.folder = Path().resolve() + + def clear_old_dist(self) -> None: + """Delete old dist folders. + + .. versionadded:: 7.5 + """ + info('Removing old dist folders... ', newline=False) + 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) + info('done') + + @abc.abstractmethod + def copy_files(self) -> None: + """Copy files.""" + + @abc.abstractmethod + def cleanup(self) -> None: + """Cleanup copied files.""" + + def run(self) -> None: + """Run the installer script.""" + if self.upgrade: + check_call('python -m pip install --upgrade pip', shell=True) + check_call( + 'pip install --upgrade setuptools wheel twine ', shell=True) + + if self.clear: + self.clear_old_dist() + + if self.nodist: + return + + self.copy_files() + try: + setup.main() # create a new package + except SystemExit as e: + error(e) + return + finally: + self.cleanup() + + if self.local: + check_call('pip uninstall pywikibot -y', shell=True) + check_call( + 'pip install --no-index --pre --find-links=dist pywikibot', + shell=True) + + if self.remote and input_yn( + '<<lightblue>>Upload dist to pypi', automatic_quit=False): + check_call('twine upload dist/*', shell=True)
-def copy_files() -> None: # pragma: no cover - """Copy code entry point and i18n files to pywikibot.scripts folder. +class SetupPywikibot(SetupBase):
- pwb.py wrapper script is a console script entry point for the - site-package. pywikibot i18n files are used for some translations. - They are copied to the pywikibot scripts folder. + """Setup for Pywikibot distribution. + + .. versionadded:: 8.0 """ - folder = Path().resolve() - info(f'directory is {folder}')
- # copy script entry points to pywikibot\scripts - target = folder / 'pywikibot' / 'scripts' - filename = 'pwb.py' - info(f'copy script entry point {filename!r} to {target}... ', - newline=False) - shutil.copy(folder / filename, target / filename) - info('done') + def __init__(self, *args) -> None: + """Set source and target directories.""" + super().__init__(*args) + source = self.folder / 'scripts' / 'i18n' / 'pywikibot' + target = self.folder / 'pywikibot' / 'scripts' / 'i18n' / 'pywikibot' + self.target = target + self.source = source
- target = target / 'i18n' / 'pywikibot' - info(f'copy i18n files to {target} ... ', newline=False) - target.parent.mkdir() - filename = '__init__.py' - shutil.copy(folder / 'scripts' / 'i18n' / filename, - target.parent / filename) - shutil.copytree(folder / 'scripts' / 'i18n' / 'pywikibot', target) - info('done') + def copy_files(self) -> None: # pragma: no cover + """Copy i18n files to pywikibot.scripts folder.
+ Pywikibot i18n files are used for some translations. They are copied + to the pywikibot scripts folder. + """ + info(f'directory is {self.folder}') + info(f'clear {self.target} directory') + shutil.rmtree(self.target, ignore_errors=True) + info('copy i18n files ... ', newline=False) + shutil.copytree(self.source, self.target) + info('done')
-def cleanup() -> None: # pragma: no cover - """Remove all files which were copied to the pywikibot scripts folder.""" - info('Remove copied files... ', newline=False) - folder = Path().resolve() - target = folder / 'pywikibot' / 'scripts' / 'i18n' - shutil.rmtree(target) - target = target.parent / 'pwb.py' - target.unlink() - info('done') + def cleanup(self) -> None: # pragma: no cover + """Remove all copied files from pywikibot scripts folder.""" + info('Remove copied files... ', newline=False) + shutil.rmtree(self.target) + # restore pywikibot en.json file + filename = 'en.json' + self.target.mkdir() + shutil.copy(self.source / filename, self.target / filename) + info('done')
def handle_args() -> Tuple[bool, bool, bool, bool, bool]: @@ -134,37 +190,10 @@ return local, remote, clear, upgrade, nodist
-def main() -> None: # pragma: no cover +def main() -> None: """Script entry point.""" - local, remote, clear, upgrade, nodist = handle_args() - - if upgrade: - check_call('python -m pip install --upgrade pip', shell=True) - check_call('pip install --upgrade setuptools wheel twine ', shell=True) - - if clear: - clear_old_dist() - - if nodist: - return - - copy_files() - try: - setup.main() # create a new package - except SystemExit as e: - error(e) - return - finally: - cleanup() - - if local: - check_call('pip uninstall pywikibot -y', shell=True) - check_call('pip install --no-index --pre --find-links=dist pywikibot', - shell=True) - - if remote and input_yn( - '<<lightblue>>Upload dist to pypi', automatic_quit=False): - check_call('twine upload dist/*', shell=True) + args = handle_args() + SetupPywikibot(*args).run()
if __name__ == '__main__': # pragma: no cover diff --git a/pwb.py b/pwb.py new file mode 100644 index 0000000..0e12919 --- /dev/null +++ b/pwb.py @@ -0,0 +1,36 @@ +"""pwb caller script to invoke the :mod:`pywikibot.scripts.wrapper` script. + +.. versionadded:: 8.0 +""" +# +# (C) Pywikibot team, 2022 +# +# Distributed under the terms of the MIT license. +# +import sys + +VERSIONS_REQUIRED_MESSAGE = """ +Pywikibot is not available on: +{version} + +This version of Pywikibot only supports Python 3.6.1+. +""" + + +def python_is_supported(): + """Check that Python is supported.""" + return sys.version_info[:3] >= (3, 6, 1) + + +if not python_is_supported(): # pragma: no cover + sys.exit(VERSIONS_REQUIRED_MESSAGE.format(version=sys.version)) + + +def main(): + """Entry point for :func:`tests.utils.execute_pwb`.""" + from pywikibot.scripts import wrapper + wrapper.main() + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/pywikibot/scripts/wrapper.py b/pywikibot/scripts/wrapper.py index 513f876..db59e06 100755 --- a/pywikibot/scripts/wrapper.py +++ b/pywikibot/scripts/wrapper.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- """Wrapper script to invoke pywikibot-based scripts.
This wrapper script invokes script by its name in this search order: @@ -35,28 +34,25 @@ pwb wrapper was added to the Python site package lib .. versionchanged:: 7.7 pwb wrapper is able to set ``PYWIKIBOT_TEST_...`` environment variables +.. versionchanged:: 8.0 + renamed to wrapper.py """ +# # (C) Pywikibot team, 2012-2022 # # Distributed under the terms of the MIT license. # -# ## KEEP PYTHON 2 SUPPORT FOR THIS SCRIPT ## # -from __future__ import print_function - +# import os import sys import types from difflib import get_close_matches from importlib import import_module +from pathlib import Path from time import sleep from warnings import warn
-try: - from pathlib import Path -except ImportError: - pass - pwb = None site_package = False
@@ -184,6 +180,8 @@ for index, arg in enumerate(args, start=1): if arg in ('-version', '--version'): fname = 'version.py' + elif arg in ('pwb', 'pwb.py', 'wrapper', 'wrapper.py'): + pass elif arg.startswith('-'): local.append(arg) elif arg.startswith('PYWIKIBOT_TEST_'): @@ -291,8 +289,9 @@ # If successful, user config file already exists in one of the candidate # directories. See config.py for details on search order. # Use env var to communicate to config.py pwb.py location (bug T74918). -_pwb_dir = os.path.split(__file__)[0] -os.environ['PYWIKIBOT_DIR_PWB'] = _pwb_dir +wrapper_dir = Path(__file__).parent +os.environ['PYWIKIBOT_DIR_PWB'] = str(wrapper_dir) + try: import pywikibot as pwb except RuntimeError as e: # pragma: no cover @@ -309,8 +308,8 @@
print('NOTE: user-config.py was not found!') print('Please follow the prompts to create it:') - run_python_file(os.path.join( - _pwb_dir, 'pywikibot', 'scripts', 'generate_user_files.py'), []) + run_python_file(str(wrapper_dir.joinpath( + 'pywikibot', 'scripts', 'generate_user_files.py')), []) # because we have loaded pywikibot without user-config.py loaded, # we need to re-start the entire process. Ask the user to do so. print('Now, you have to re-execute the command to start your script.') @@ -335,12 +334,10 @@
scripts = {}
- script_paths = [['.']] + script_paths # add current directory - for path in script_paths: - folder = Path(_pwb_dir).joinpath(*path) + for folder in script_paths: if not folder.exists(): # pragma: no cover - warning('{} does not exists; remove it from user_script_paths' - .format(folder)) + warning( + f'{folder} does not exists; remove it from user_script_paths') continue for script_name in folder.iterdir(): name, suffix = script_name.stem, script_name.suffix @@ -387,27 +384,17 @@ from pywikibot import config path_list = [] # paths to find misspellings
- def test_paths(paths, root): + def test_paths(paths, root: Path): """Search for filename in given paths within 'root' base directory.""" for file_package in paths: package = file_package.split('.') path = package + [filename] - testpath = os.path.join(root, *path) - if os.path.exists(testpath): - return testpath - path_list.append(package) + testpath = root.joinpath(*path) + if testpath.exists(): + return str(testpath) + path_list.append(testpath.parent) return None
- if site_package: # pragma: no cover - script_paths = [''] # only use the root as path - else: - script_paths = [ - 'scripts.userscripts', - 'scripts', - 'scripts.maintenance', - 'pywikibot.scripts', - ] - user_script_paths = [] if config.user_script_paths: # pragma: no cover if isinstance(config.user_script_paths, list): @@ -417,14 +404,25 @@ 'found: {}. Ignoring this setting.' .format(type(config.user_script_paths)))
- found = test_paths(user_script_paths, config.base_dir) + found = test_paths(user_script_paths, Path(config.base_dir)) if found: # pragma: no cover return found
- found = test_paths(script_paths, _pwb_dir) + # search for system scripts in pywikibot.scripts directory + found = test_paths([''], wrapper_dir) if found: return found
+ if not site_package: + script_paths = [ + 'scripts', + 'scripts.maintenance', + 'pywikibot.scripts', + ] + found = test_paths(script_paths, wrapper_dir.parents[1]) + if found: + return found + return find_alternates(filename, path_list)
@@ -496,14 +494,8 @@ .. versionchanged:: 7.0 previous implementation was renamed to :func:`execute` """ - try: - if not check_modules(): # pragma: no cover - raise RuntimeError('') # no further output needed - # setup.py may also raise RuntimeError - except RuntimeError as e: # pragma: no cover - sys.exit(e) - except SyntaxError as e: # pragma: no cover - sys.exit(str(e) + '\nProbably outdated Python version') + if not check_modules(): # pragma: no cover + sys.exit()
if not execute(): print(__doc__) diff --git a/setup.py b/setup.py index cfd23a5..ae5928d 100755 --- a/setup.py +++ b/setup.py @@ -27,6 +27,7 @@ import os import re import sys +from setuptools import setup
if sys.version_info[:3] >= (3, 9): List = list @@ -34,28 +35,6 @@ from typing import List
-VERSIONS_REQUIRED_MESSAGE = """ -Pywikibot is not available on: -{version} - -This version of Pywikibot only supports Python 3.6.1+. -""" - -try: - from setuptools import setup -except SyntaxError: - raise RuntimeError(VERSIONS_REQUIRED_MESSAGE.format(version=sys.version)) - - -def python_is_supported() -> bool: - """Check that Python is supported.""" - return sys.version_info[:3] >= (3, 6, 1) - - -if not python_is_supported(): # pragma: no cover - # pwb.py checks this exception - raise RuntimeError(VERSIONS_REQUIRED_MESSAGE.format(version=sys.version)) - # ------- setup extra_requires ------- # extra_deps = { # Core library dependencies