1# Licensed under a 3-clause BSD style license - see LICENSE.rst
2"""
3Astropy is a package intended to contain core functionality and some
4common tools needed for performing astronomy and astrophysics research with
5Python. It also provides an index for other astronomy packages and tools for
6managing them.
7"""
8
9import sys
10import os
11from warnings import warn
12
13from .version import version as __version__
14
15
16def _is_astropy_source(path=None):
17    """
18    Returns whether the source for this module is directly in an astropy
19    source distribution or checkout.
20    """
21
22    # If this __init__.py file is in ./astropy/ then import is within a source
23    # dir .astropy-root is a file distributed with the source, but that should
24    # not installed
25    if path is None:
26        path = os.path.join(os.path.dirname(__file__), os.pardir)
27    elif os.path.isfile(path):
28        path = os.path.dirname(path)
29
30    source_dir = os.path.abspath(path)
31    return os.path.exists(os.path.join(source_dir, '.astropy-root'))
32
33
34# The location of the online documentation for astropy
35# This location will normally point to the current released version of astropy
36if 'dev' in __version__:
37    online_docs_root = 'https://docs.astropy.org/en/latest/'
38else:
39    online_docs_root = f'https://docs.astropy.org/en/{__version__}/'
40
41
42from . import config as _config  # noqa: E402
43
44
45class Conf(_config.ConfigNamespace):
46    """
47    Configuration parameters for `astropy`.
48    """
49
50    unicode_output = _config.ConfigItem(
51        False,
52        'When True, use Unicode characters when outputting values, and '
53        'displaying widgets at the console.')
54    use_color = _config.ConfigItem(
55        sys.platform != 'win32',
56        'When True, use ANSI color escape sequences when writing to the console.',
57        aliases=['astropy.utils.console.USE_COLOR', 'astropy.logger.USE_COLOR'])
58    max_lines = _config.ConfigItem(
59        None,
60        description='Maximum number of lines in the display of pretty-printed '
61        'objects. If not provided, try to determine automatically from the '
62        'terminal size.  Negative numbers mean no limit.',
63        cfgtype='integer(default=None)',
64        aliases=['astropy.table.pprint.max_lines'])
65    max_width = _config.ConfigItem(
66        None,
67        description='Maximum number of characters per line in the display of '
68        'pretty-printed objects.  If not provided, try to determine '
69        'automatically from the terminal size. Negative numbers mean no '
70        'limit.',
71        cfgtype='integer(default=None)',
72        aliases=['astropy.table.pprint.max_width'])
73
74
75conf = Conf()
76
77
78# Define a base ScienceState for configuring constants and units
79from .utils.state import ScienceState  # noqa: E402
80
81
82class base_constants_version(ScienceState):
83    """
84    Base class for the real version-setters below
85    """
86    _value = 'test'
87
88    _versions = dict(test='test')
89
90    @classmethod
91    def validate(cls, value):
92        if value not in cls._versions:
93            raise ValueError(f'Must be one of {list(cls._versions.keys())}')
94        return cls._versions[value]
95
96    @classmethod
97    def set(cls, value):
98        """
99        Set the current constants value.
100        """
101        import sys
102        if 'astropy.units' in sys.modules:
103            raise RuntimeError('astropy.units is already imported')
104        if 'astropy.constants' in sys.modules:
105            raise RuntimeError('astropy.constants is already imported')
106
107        return super().set(value)
108
109
110class physical_constants(base_constants_version):
111    """
112    The version of physical constants to use
113    """
114    # Maintainers: update when new constants are added
115    _value = 'codata2018'
116
117    _versions = dict(codata2018='codata2018', codata2014='codata2014',
118                     codata2010='codata2010', astropyconst40='codata2018',
119                     astropyconst20='codata2014', astropyconst13='codata2010')
120
121
122class astronomical_constants(base_constants_version):
123    """
124    The version of astronomical constants to use
125    """
126    # Maintainers: update when new constants are added
127    _value = 'iau2015'
128
129    _versions = dict(iau2015='iau2015', iau2012='iau2012',
130                     astropyconst40='iau2015', astropyconst20='iau2015',
131                     astropyconst13='iau2012')
132
133
134# Create the test() function
135from .tests.runner import TestRunner  # noqa: E402
136test = TestRunner.make_test_runner_in(__path__[0])
137
138
139# if we are *not* in setup mode, import the logger and possibly populate the
140# configuration file with the defaults
141def _initialize_astropy():
142    try:
143        from .utils import _compiler  # noqa: F401
144    except ImportError:
145        if _is_astropy_source():
146            raise ImportError('You appear to be trying to import astropy from '
147                              'within a source checkout or from an editable '
148                              'installation without building the extension '
149                              'modules first. Either run:\n\n'
150                              '  pip install -e .\n\nor\n\n'
151                              '  python setup.py build_ext --inplace\n\n'
152                              'to make sure the extension modules are built ')
153        else:
154            # Outright broken installation, just raise standard error
155            raise
156
157
158# Set the bibtex entry to the article referenced in CITATION.
159def _get_bibtex():
160    citation_file = os.path.join(os.path.dirname(__file__), 'CITATION')
161
162    with open(citation_file, 'r') as citation:
163        refs = citation.read().split('@ARTICLE')[1:]
164        if len(refs) == 0:
165            return ''
166        bibtexreference = f'@ARTICLE{refs[0]}'
167    return bibtexreference
168
169
170__citation__ = __bibtex__ = _get_bibtex()
171
172from .logger import _init_log, _teardown_log  # noqa: E402, F401
173
174log = _init_log()
175
176_initialize_astropy()
177
178from .utils.misc import find_api_page  # noqa: E402, F401
179
180
181def online_help(query):
182    """
183    Search the online Astropy documentation for the given query.
184    Opens the results in the default web browser.  Requires an active
185    Internet connection.
186
187    Parameters
188    ----------
189    query : str
190        The search query.
191    """
192    from urllib.parse import urlencode
193    import webbrowser
194
195    version = __version__
196    if 'dev' in version:
197        version = 'latest'
198    else:
199        version = 'v' + version
200
201    url = f"https://docs.astropy.org/en/{version}/search.html?{urlencode({'q': query})}"
202    webbrowser.open(url)
203
204
205__dir_inc__ = ['__version__', '__githash__',
206               '__bibtex__', 'test', 'log', 'find_api_page', 'online_help',
207               'online_docs_root', 'conf', 'physical_constants',
208               'astronomical_constants']
209
210
211from types import ModuleType as __module_type__  # noqa: E402
212# Clean up top-level namespace--delete everything that isn't in __dir_inc__
213# or is a magic attribute, and that isn't a submodule of this package
214for varname in dir():
215    if not ((varname.startswith('__') and varname.endswith('__')) or
216            varname in __dir_inc__ or
217            (varname[0] != '_' and
218                isinstance(locals()[varname], __module_type__) and
219                locals()[varname].__name__.startswith(__name__ + '.'))):
220        # The last clause in the the above disjunction deserves explanation:
221        # When using relative imports like ``from .. import config``, the
222        # ``config`` variable is automatically created in the namespace of
223        # whatever module ``..`` resolves to (in this case astropy).  This
224        # happens a few times just in the module setup above.  This allows
225        # the cleanup to keep any public submodules of the astropy package
226        del locals()[varname]
227
228del varname, __module_type__
229