[cctbxbb] [git/cctbx] master: Generate libtbx.* wrappers from correct directory (ad736e2c8)

Billy Poon BKPoon at lbl.gov
Fri Jun 1 13:32:24 PDT 2018


I don't think this is working on macOS. The configuration step is
generating error messages like,

Ignored: [Errno 2] No such file or directory: <command path>

In lines 1640-1643,

self.write_dispatcher(
    source_file=libtbx.env.under_base(os.path.join('bin', ep.name)),
    target_file=os.path.join('bin', 'libtbx.' + ep.name),
)

the source_file argument is always being constructed as
<base>/bin/<command> instead of using the directory from
self.get_setuptools_script_dir(). Should it be the bin_directory variable
instead (e.g. source_file=os.path.join(bin_directory, ep.name))?

As a side note, conda will put everything in bin because there is no
framework. For access to the window manager, you would call pythonw, which
points to a python inside a .app bundle.


--
Billy K. Poon
Research Scientist, Molecular Biophysics and Integrated Bioimaging
Lawrence Berkeley National Laboratory
1 Cyclotron Road, M/S 33R0345
Berkeley, CA 94720
Tel: (510) 486-5709
Fax: (510) 486-5909
Web: https://phenix-online.org


On Fri, Jun 1, 2018 at 3:15 AM CCTBX commit <
diamondlightsource.jenkins at gmail.com> wrote:

> Repository : ssh://g18-sc-serv-04.diamond.ac.uk/cctbx
> On branch  : master
>
> ------------------------------
>
>
> commit ad736e2c8e65407615a7e6604b0544687ed8058c
> Author: Markus Gerstel <markus.gerstel at diamond.ac.uk>
> Date:   Thu May 31 08:27:09 2018 +0100
>
>     Generate libtbx.* wrappers from correct directory
>
>     Python console scripts don't necessarily land in base/bin, specifically
>     on MacOS this is not the case. Use setuptools etc. to find the actual
>     Python console script location and use that directory instead. Fixes
> #177
>
>     Add a function to regenerate all Python console scripts which can be
>     called from an installer to relocate paths.
>
>
> ------------------------------
>
>
> ad736e2c8e65407615a7e6604b0544687ed8058c
> libtbx/env_config.py      |  74 +++++++++++++++++++++++++-----
> libtbx/fastentrypoints.py | 112
> ++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 176 insertions(+), 10 deletions(-)
>
> diff --git a/libtbx/env_config.py b/libtbx/env_config.py
> index de5fc72dd..25daa2cb3 100644
> --- a/libtbx/env_config.py
> +++ b/libtbx/env_config.py
> @@ -1562,23 +1562,77 @@ selfx:
>            source_file=source_file,
>            target_file=module_name+"."+command)
>
> +  def get_setuptools_script_dir():
> +    '''
> +    Find the location of python entry point console_scripts, ie. things
> like
> +    'pip', 'pytest', ...
> +    This is different from simple /base/bin, eg. on MacOS.
> +
> +
> https://stackoverflow.com/questions/25066084/get-entry-point-script-file-location-in-setuputils-package
> +    '''
> +    from setuptools import Distribution
> +    from setuptools.command.install import install
> +    class OnlyGetScriptPath(install):
> +      def run(self):
> +        # does not call install.run() by design
> +        self.distribution.install_scripts = self.install_scripts
> +    dist = Distribution({'cmdclass': {'install': OnlyGetScriptPath}})
> +    dist.dry_run = True  # not sure if necessary, but to be safe
> +    dist.parse_config_files()
> +    command = dist.get_command_obj('install')
> +    command.ensure_finalized()
> +    command.run()
> +    return dist.install_scripts
> +
> +  def regenerate_entry_point_console_scripts(self, verbose=True):
> +    '''
> +    Creates all console_scripts entry point scripts from scratch and
> overwrites existing ones.
> +    This is intended to be used by installers to relocate the entry point
> script paths.
> +    '''
> +    try:
> +      import distutils.dist
> +      import libtbx.fastentrypoints # monkeypatches setuptools
> +      import pkg_resources
> +      import setuptools.command.easy_install
> +    except ImportError:
> +      return
> +
> +    # Prepare generic script generator
> +    distribution = distutils.dist.Distribution({'name': 'setuptools'})
> +    command = setuptools.command.easy_install.easy_install(distribution)
> +    command.args = ['wheel']  # dummy argument
> +    command.finalize_options()
> +
> +    # Force regeneration of all known console_scripts
> +    for pkg_resources_dist in pkg_resources.working_set:
> +      console_scripts =
> pkg_resources_dist.get_entry_map().get('console_scripts')
> +      if console_scripts:
> +        if verbose:
> +          print("Regenerating commands for %s: %s" % (
> +              pkg_resources_dist,
> +              list(console_scripts),
> +          ))
> +        command.install_wrapper_scripts(pkg_resources_dist)
> +
>    def generate_entry_point_dispatchers(self):
> -    # Write indirect dispatcher scripts for all console_scripts entry
> points
> -    # that have existing dispatcher scripts in the base/bin directory, but
> -    # add a 'libtbx.' prefix.
> -    base_bin_directory = libtbx.env.under_base('bin')
> -    if not os.path.isdir(base_bin_directory):
> +    '''
> +    Write indirect dispatcher scripts for all console_scripts entry points
> +    that have existing dispatcher scripts in the base/bin directory, but
> +    add a 'libtbx.' prefix.
> +    '''
> +    try:
> +      import pkg_resources
> +      bin_directory = get_setuptools_script_dir()
> +    except ImportError:
> +      return
> +    if not os.path.isdir(bin_directory):
>        return # do not create console_scripts dispatchers, only point to
> them
>
> -    base_bin_dispatchers = set(os.listdir(base_bin_directory))
> +    base_bin_dispatchers = set(os.listdir(bin_directory))
>      existing_dispatchers = filter(lambda f: f.startswith('libtbx.'),
> self.bin_path.listdir())
>      existing_dispatchers = set(map(lambda f: f[7:], existing_dispatchers))
>      entry_point_candidates = base_bin_dispatchers - existing_dispatchers
>
> -    try:
> -      import pkg_resources
> -    except ImportError:
> -      return
>      entry_points = pkg_resources.iter_entry_points('console_scripts')
>      entry_points = filter(lambda ep: ep.name in entry_point_candidates,
> entry_points)
>      for ep in entry_points:
> diff --git a/libtbx/fastentrypoints.py b/libtbx/fastentrypoints.py
> new file mode 100644
> index 000000000..9707f74a3
> --- /dev/null
> +++ b/libtbx/fastentrypoints.py
> @@ -0,0 +1,112 @@
> +# noqa: D300,D400
> +# Copyright (c) 2016, Aaron Christianson
> +# All rights reserved.
> +#
> +# Redistribution and use in source and binary forms, with or without
> +# modification, are permitted provided that the following conditions are
> +# met:
> +#
> +# 1. Redistributions of source code must retain the above copyright
> +#    notice, this list of conditions and the following disclaimer.
> +#
> +# 2. Redistributions in binary form must reproduce the above copyright
> +#    notice, this list of conditions and the following disclaimer in the
> +#    documentation and/or other materials provided with the distribution.
> +#
> +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
> +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
> +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
> +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
> +# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
> +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
> +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
> +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
> +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> +'''
> +Monkey patch setuptools to write faster console_scripts with this format:
> +
> +    import sys
> +    from mymodule import entry_function
> +    sys.exit(entry_function())
> +
> +This is better.
> +
> +(c) 2016, Aaron Christianson
> +http://github.com/ninjaaron/fast-entry_points
> +'''
> +from setuptools.command import easy_install
> +import re
> +TEMPLATE = '''\
> +# -*- coding: utf-8 -*-
> +# EASY-INSTALL-ENTRY-SCRIPT: '{3}','{4}','{5}'
> +__requires__ = '{3}'
> +import re
> +import sys
> +
> +from {0} import {1}
> +
> +if __name__ == '__main__':
> +    sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
> +    sys.exit({2}())'''
> +
> +
> + at classmethod
> +def get_args(cls, dist, header=None):  # noqa: D205,D400
> +    """
> +    Yield write_script() argument tuples for a distribution's
> +    console_scripts and gui_scripts entry points.
> +    """
> +    if header is None:
> +        # pylint: disable=E1101
> +        header = cls.get_header()
> +    spec = str(dist.as_requirement())
> +    for type_ in 'console', 'gui':
> +        group = type_ + '_scripts'
> +        for name, ep in dist.get_entry_map(group).items():
> +            # ensure_safe_name
> +            if re.search(r'[\\/]', name):
> +                raise ValueError("Path separators not allowed in script
> names")
> +            script_text = TEMPLATE.format(
> +                ep.module_name, ep.attrs[0], '.'.join(ep.attrs),
> +                spec, group, name)
> +            # pylint: disable=E1101
> +            args = cls._get_script_args(type_, name, header, script_text)
> +            for res in args:
> +                yield res
> +
> +
> +# pylint: disable=E1101
> +easy_install.ScriptWriter.get_args = get_args
> +
> +
> +def main():
> +    import os
> +    import re
> +    import shutil
> +    import sys
> +    dests = sys.argv[1:] or ['.']
> +    filename = re.sub('\.pyc$', '.py', __file__)
> +
> +    for dst in dests:
> +        shutil.copy(filename, dst)
> +        manifest_path = os.path.join(dst, 'MANIFEST.in')
> +        setup_path = os.path.join(dst, 'setup.py')
> +
> +        # Insert the include statement to MANIFEST.in if not present
> +        with open(manifest_path, 'a+') as manifest:
> +            manifest.seek(0)
> +            manifest_content = manifest.read()
> +            if 'include fastentrypoints.py' not in manifest_content:
> +                manifest.write(('\n' if manifest_content else '') +
> +                               'include fastentrypoints.py')
> +
> +        # Insert the import statement to setup.py if not present
> +        with open(setup_path, 'a+') as setup:
> +            setup.seek(0)
> +            setup_content = setup.read()
> +            if 'import fastentrypoints' not in setup_content:
> +                setup.seek(0)
> +                setup.truncate()
> +                setup.write('import fastentrypoints\n' + setup_content)
>
> ------------------------------
>
> To unsubscribe from the CCTBX-COMMIT list, click the following link:
> https://www.jiscmail.ac.uk/cgi-bin/webadmin?SUBED1=CCTBX-COMMIT&A=1
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://phenix-online.org/pipermail/cctbxbb/attachments/20180601/9dc0a032/attachment-0001.htm>


More information about the cctbxbb mailing list