<commit_before># Licensed under a 3-clause BSD style license - see LICENSE.rst
"""
This module contains a number of utilities for use during
setup/build/packaging that are useful to astropy as a whole.
"""

from __future__ import absolute_import

import imp
import os
import shutil
import sys
import re
import shlex

from distutils import log
from distutils.dist import Distribution
from distutils.errors import DistutilsError
from distutils.core import Extension
from distutils.log import warn
from distutils.command.build import build as DistutilsBuild

from .tests.helper import astropy_test


try:
    import Cython
    HAVE_CYTHON = True
except ImportError:
    HAVE_CYTHON = False

try:
    from numpy import get_include as get_numpy_include
    numpy_includes = get_numpy_include()
except ImportError:
    numpy_includes = []


class AstropyBuild(DistutilsBuild):
    """
    A custom 'build' command that adds the following options:

        `--disable-legacy` : Disable installing legacy shims
    """
    user_options = DistutilsBuild.user_options + [
        ('disable-legacy', None,
         "Don't install legacy shims")]

    boolean_options = DistutilsBuild.boolean_options + ['disable-legacy']

    def initialize_options(self):
        DistutilsBuild.initialize_options(self)
        # Set a default value for disable_legacy
        self.disable_legacy = False


try:
    from sphinx.setup_command import BuildDoc

    class AstropyBuildSphinx(BuildDoc):
        """ A version of the ``build_sphinx`` command that uses the
        version of Astropy that is built by the setup ``build`` command,
        rather than whatever is installed on the system - to build docs
        against the installed version, run ``make html`` in the
        ``astropy/docs`` directory.

        This also automatically creates the docs/_static directories -
        this is needed because github won't create the _static dir
        because it has no tracked files.
        """

        description = 'Build Sphinx documentation for Astropy environment'
        user_options = BuildDoc.user_options[:]
        user_options.append(('clean-docs', 'l', 'Clean previously-built docs '
                                                'before building new ones'))
        boolean_options = BuildDoc.boolean_options[:]
        boolean_options.append('clean-docs')

        _self_iden_rex = re.compile(r"self\.([^\d\W][\w]+)", re.UNICODE)

        def initialize_options(self):
            BuildDoc.initialize_options(self)
            self.clean_docs = False

        def finalize_options(self):
            from os.path import isdir
            from shutil import rmtree

            #Clear out previous sphinx builds, if requested
            if self.clean_docs:
                dirstorm = ['docs/_generated']
                if self.build_dir is None:
                    dirstorm.append('docs/_build')
                else:
                    dirstorm.append(self.build_dir)

                for d in dirstorm:
                    if isdir(d):
                        log.info('Cleaning directory ' + d)
                        rmtree(d)
                    else:
                        log.info('Not cleaning directory ' + d + ' because '
                                 'not present or not a directory')

            BuildDoc.finalize_options(self)

        def run(self):
            from os.path import split, join
            from distutils.cmd import DistutilsOptionError
            from subprocess import Popen, PIPE
            from textwrap import dedent
            from inspect import getsourcelines

            # If possible, create the _static dir
            if self.build_dir is not None:
                # the _static dir should be in the same place as the _build dir
                # for Astropy
                basedir, subdir = split(self.build_dir)
                if subdir == '':  # the path has a trailing /...
                    basedir, subdir = split(basedir)
                staticdir = join(basedir, '_static')
                if os.path.isfile(staticdir):
                    raise DistutilsOptionError(
                        'Attempted to build_sphinx in a location where' +
                        staticdir + 'is a file.  Must be a directory.')
                self.mkpath(staticdir)

            #Now make sure Astropy is built and determine where it was built
            build_cmd = self.reinitialize_command('build')
            build_cmd.inplace = 0
            self.run_command('build')
            build_cmd = self.get_finalized_command('build')
            build_cmd_path = os.path.abspath(build_cmd.build_lib)

            #Now generate the source for and spawn a new process that runs the
            #command.  This is needed to get the correct imports for the built
            #version

            runlines, runlineno = getsourcelines(BuildDoc.run)
            subproccode = dedent("""
            from sphinx.setup_command import *

            os.chdir('{srcdir}')
            sys.path.insert(0,'{build_cmd_path}')

            """).format(build_cmd_path=build_cmd_path, srcdir=self.source_dir)
            #runlines[1:] removes 'def run(self)' on the first line
            subproccode += dedent(''.join(runlines[1:]))

            # All "self.foo" in the subprocess code needs to be replaced by the
            # values taken from the current self in *this* process
            subproccode = AstropyBuildSphinx._self_iden_rex.split(subproccode)
            for i in range(1, len(subproccode), 2):
                iden = subproccode[i]
                val = getattr(self, iden)
                if iden.endswith('_dir'):
                    #Directories should be absolute, because the `chdir` call
                    #in the new process moves to a different directory
                    subproccode[i] = repr(os.path.abspath(val))
                else:
                    subproccode[i] = repr(val)
            subproccode = ''.join(subproccode)

            log.debug('Starting subprocess of {0} with python code:\n{1}\n'
                      '[CODE END])'.format(sys.executable, subproccode))

            proc = Popen([sys.executable], stdin=PIPE)
            proc.communicate(subproccode)
            if proc.returncode != 0:
                log.warn('Sphinx Documentation subprocess failed with return '
                         'code ' + str(proc.returncode))

except ImportError as e:
    if 'sphinx' in e.args[0]:  # Sphinx not present
        AstropyBuildSphinx = None
    else:
        raise


def get_distutils_display_options():
    """ Returns a set of all the distutils display options in their long and
    short forms.  These are the setup.py arguments such as --name or --version
    which print the project's metadata and then exit.

    Returns
    -------
    opts : set
        The long and short form display option arguments, including the - or --
    """

    short_display_opts = set('-' + o[1] for o in Distribution.display_options
                             if o[1])
    long_display_opts = set('--' + o[0] for o in Distribution.display_options)

    return short_display_opts.union(long_display_opts)


def is_distutils_display_option():
    """ Returns True if sys.argv contains any of the distutils display options
    such as --version or --name.
    """

    display_options = get_distutils_display_options()
    return bool(set(sys.argv[1:]).intersection(display_options))<commit_msg>Fixes #300 by including -h and --help as distutils display options<commit_after># Licensed under a 3-clause BSD style license - see LICENSE.rst
"""
This module contains a number of utilities for use during
setup/build/packaging that are useful to astropy as a whole.
"""

from __future__ import absolute_import

import imp
import os
import shutil
import sys
import re
import shlex

from distutils import log
from distutils.dist import Distribution
from distutils.errors import DistutilsError
from distutils.core import Extension
from distutils.log import warn
from distutils.command.build import build as DistutilsBuild

from .tests.helper import astropy_test


try:
    import Cython
    HAVE_CYTHON = True
except ImportError:
    HAVE_CYTHON = False

try:
    from numpy import get_include as get_numpy_include
    numpy_includes = get_numpy_include()
except ImportError:
    numpy_includes = []


class AstropyBuild(DistutilsBuild):
    """
    A custom 'build' command that adds the following options:

        `--disable-legacy` : Disable installing legacy shims
    """
    user_options = DistutilsBuild.user_options + [
        ('disable-legacy', None,
         "Don't install legacy shims")]

    boolean_options = DistutilsBuild.boolean_options + ['disable-legacy']

    def initialize_options(self):
        DistutilsBuild.initialize_options(self)
        # Set a default value for disable_legacy
        self.disable_legacy = False


try:
    from sphinx.setup_command import BuildDoc

    class AstropyBuildSphinx(BuildDoc):
        """ A version of the ``build_sphinx`` command that uses the
        version of Astropy that is built by the setup ``build`` command,
        rather than whatever is installed on the system - to build docs
        against the installed version, run ``make html`` in the
        ``astropy/docs`` directory.

        This also automatically creates the docs/_static directories -
        this is needed because github won't create the _static dir
        because it has no tracked files.
        """

        description = 'Build Sphinx documentation for Astropy environment'
        user_options = BuildDoc.user_options[:]
        user_options.append(('clean-docs', 'l', 'Clean previously-built docs '
                                                'before building new ones'))
        boolean_options = BuildDoc.boolean_options[:]
        boolean_options.append('clean-docs')

        _self_iden_rex = re.compile(r"self\.([^\d\W][\w]+)", re.UNICODE)

        def initialize_options(self):
            BuildDoc.initialize_options(self)
            self.clean_docs = False

        def finalize_options(self):
            from os.path import isdir
            from shutil import rmtree

            #Clear out previous sphinx builds, if requested
            if self.clean_docs:
                dirstorm = ['docs/_generated']
                if self.build_dir is None:
                    dirstorm.append('docs/_build')
                else:
                    dirstorm.append(self.build_dir)

                for d in dirstorm:
                    if isdir(d):
                        log.info('Cleaning directory ' + d)
                        rmtree(d)
                    else:
                        log.info('Not cleaning directory ' + d + ' because '
                                 'not present or not a directory')

            BuildDoc.finalize_options(self)

        def run(self):
            from os.path import split, join
            from distutils.cmd import DistutilsOptionError
            from subprocess import Popen, PIPE
            from textwrap import dedent
            from inspect import getsourcelines

            # If possible, create the _static dir
            if self.build_dir is not None:
                # the _static dir should be in the same place as the _build dir
                # for Astropy
                basedir, subdir = split(self.build_dir)
                if subdir == '':  # the path has a trailing /...
                    basedir, subdir = split(basedir)
                staticdir = join(basedir, '_static')
                if os.path.isfile(staticdir):
                    raise DistutilsOptionError(
                        'Attempted to build_sphinx in a location where' +
                        staticdir + 'is a file.  Must be a directory.')
                self.mkpath(staticdir)

            #Now make sure Astropy is built and determine where it was built
            build_cmd = self.reinitialize_command('build')
            build_cmd.inplace = 0
            self.run_command('build')
            build_cmd = self.get_finalized_command('build')
            build_cmd_path = os.path.abspath(build_cmd.build_lib)

            #Now generate the source for and spawn a new process that runs the
            #command.  This is needed to get the correct imports for the built
            #version

            runlines, runlineno = getsourcelines(BuildDoc.run)
            subproccode = dedent("""
            from sphinx.setup_command import *

            os.chdir('{srcdir}')
            sys.path.insert(0,'{build_cmd_path}')

            """).format(build_cmd_path=build_cmd_path, srcdir=self.source_dir)
            #runlines[1:] removes 'def run(self)' on the first line
            subproccode += dedent(''.join(runlines[1:]))

            # All "self.foo" in the subprocess code needs to be replaced by the
            # values taken from the current self in *this* process
            subproccode = AstropyBuildSphinx._self_iden_rex.split(subproccode)
            for i in range(1, len(subproccode), 2):
                iden = subproccode[i]
                val = getattr(self, iden)
                if iden.endswith('_dir'):
                    #Directories should be absolute, because the `chdir` call
                    #in the new process moves to a different directory
                    subproccode[i] = repr(os.path.abspath(val))
                else:
                    subproccode[i] = repr(val)
            subproccode = ''.join(subproccode)

            log.debug('Starting subprocess of {0} with python code:\n{1}\n'
                      '[CODE END])'.format(sys.executable, subproccode))

            proc = Popen([sys.executable], stdin=PIPE)
            proc.communicate(subproccode)
            if proc.returncode != 0:
                log.warn('Sphinx Documentation subprocess failed with return '
                         'code ' + str(proc.returncode))

except ImportError as e:
    if 'sphinx' in e.args[0]:  # Sphinx not present
        AstropyBuildSphinx = None
    else:
        raise


def get_distutils_display_options():
    """ Returns a set of all the distutils display options in their long and
    short forms.  These are the setup.py arguments such as --name or --version
    which print the project's metadata and then exit.

    Returns
    -------
    opts : set
        The long and short form display option arguments, including the - or --
    """

    short_display_opts = set('-' + o[1] for o in Distribution.display_options
                             if o[1])
    long_display_opts = set('--' + o[0] for o in Distribution.display_options)

    # Include -h and --help which are not explicitly listed in
    # Distribution.display_options (as they are handled by optparse)
    short_display_opts.add('-h')
    long_display_opts.add('--help')

    return short_display_opts.union(long_display_opts)


def is_distutils_display_option():
    """ Returns True if sys.argv contains any of the distutils display options
    such as --version or --name.
    """

    display_options = get_distutils_display_options()
    return bool(set(sys.argv[1:]).intersection(display_options))
{'fraction_non_alphanumeric': 0.07870339930304425, 'fraction_numerical': 0.0021697679005851797, 'mean_word_length': 2.0671506352087112, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2938524', 'n_tokens_mistral': 4146, 'n_tokens_neox': 3959, 'n_words': 1288}