1
2#A* -------------------------------------------------------------------
3#B* This file contains source code for the PyMOL computer program
4#C* Copyright (c) Schrodinger, LLC.
5#D* -------------------------------------------------------------------
6#E* It is unlawful to modify or remove this copyright notice.
7#F* -------------------------------------------------------------------
8#G* Please see the accompanying LICENSE file for further information.
9#H* -------------------------------------------------------------------
10#I* Additional authors of this source file include:
11#-*
12#-*
13#-*
14#Z* -------------------------------------------------------------------
15
16# cmd.py
17# Python interface module for PyMol
18#
19# **This is the only module which should be/need be imported by
20# ** PyMol API Based Programs
21
22# NEW CALL RETURN CONVENTIONS for _cmd.so C-layer
23#
24
25# (1) Calls into C (_cmd) should return results/status and print
26#     errors and feedback (according to mask) BUT NEVER RAISE EXCEPTIONS
27#     from within the C code itself.
28
29# (2) Effective with version 0.99, standard Python return conventions
30# apply, but haven't yet been fully implemented.  In summary:
31
32#     Unless explicitly specified in the function:
33
34#     ==> Success with no information should return None
35
36#     ==> Failure should return a negative number as follows:
37#        -1 = a general, unspecified failure
38
39#     Upon an error, exceptions will be raised by the Python wrapper
40#     layer if the "raise_exceptions" setting is on.
41
42#     ==> Boolean queries should return 1 for true/yes and 0 for false/no.
43
44#     ==> Count queries should return 0 or a positive number
45
46# (3) If _cmd produces a specific return result, be sure to include an
47#     error result as one of the possibilities outside the range of the
48#     expected return value.  For example, a negative distance
49#
50# (4) cmd.py API wrappers can then raise exceptions and return values.
51#
52# NOTE: Output tweaking via the "quiet" parameter of API functions.
53#
54# Many PyMOL API functions have a "quiet" parameter which is used to
55# customize output depending on when and where the routine was called.
56#
57# As defined, quiet should be 1.  Called from an external module, output
58# to the console should be minimal.
59#
60# However, when a command is run through the parser (often manually) the
61# user expects a little more feedback.  The parser will automatically
62# set "quiet" to zero
63#
64# In rare cases, certain nonserious error or warning output should
65# also be suppressed.  Set "quiet" to 2 for this behavior.
66
67from __future__ import print_function
68
69def _deferred_init_pymol_internals(_pymol):
70    # set up some global session tasks
71
72    if viewing.session_restore_views not in _pymol._session_restore_tasks:
73        _pymol._session_restore_tasks.append(viewing.session_restore_views)
74
75    if viewing.session_save_views not in _pymol._session_save_tasks:
76        _pymol._session_save_tasks.append(viewing.session_save_views)
77
78    if viewing.session_restore_scenes not in _pymol._session_restore_tasks:
79        _pymol._session_restore_tasks.append(viewing.session_restore_scenes)
80
81    if wizarding.session_restore_wizard not in _pymol._session_restore_tasks:
82        _pymol._session_restore_tasks.append(wizarding.session_restore_wizard)
83
84    if wizarding.session_save_wizard not in _pymol._session_save_tasks:
85        _pymol._session_save_tasks.append(wizarding.session_save_wizard)
86
87    # take care of some deferred initialization
88
89    _pymol._view_dict_sc = Shortcut({})
90    _pymol._scene_dict_sc = Shortcut({})
91
92    #
93if True:
94
95    import sys
96
97    # pymol2.cmd2 exposes a weak reference, this is not possible (and not
98    # necessary) on the module level, so we simply make a self reference.
99    _weakrefproxy = sys.modules[__name__]
100
101    if True:
102
103        import re
104        from pymol import _cmd
105        import string
106        import threading
107        import pymol
108        import os
109        from . import parsing
110        import time
111
112        _pymol = pymol
113
114        from .shortcut import Shortcut
115
116        from chempy import io
117
118        #######################################################################
119        # symbols for early export
120        #######################################################################
121
122        from .constants import *
123        from .constants import _load2str
124
125        fb_debug = sys.stderr # can redirect python debugging output elsewhere if desred...
126
127        #--------------------------------------------------------------------
128        # convenient type and result checking
129
130        from .checking import *
131        from .checking import _raising
132
133        #-------------------------------------------------------------------
134        # path expansion, including our fixes for Win32
135
136        def _nt_expandvars(path): # allow for //share/folder$/file
137            path = nt_hidden_path_re.sub(r"$$\\",path)
138            return os.path.expandvars(path)
139
140        if "nt" in sys.builtin_module_names:
141            _expandvars = _nt_expandvars
142        else:
143            _expandvars = os.path.expandvars
144
145        def exp_path(path):
146            path = as_pathstr(path)
147            return _expandvars(os.path.expanduser(path))
148
149        def as_pathstr(path):
150            # On Windows, always work with unicode file names. On Unix,
151            # UTF-8 byte strings seem to be fine, so keep them for now.
152            if isinstance(path, bytes) and pymol.IS_WINDOWS:
153                for encoding in ('utf-8', 'mbcs'):
154                    try:
155                        return path.decode(encoding)
156                    except UnicodeError:
157                        pass
158            return path
159
160        #--------------------------------------------------------------------
161        # locks and threading
162
163        reaper = None
164
165        # the following locks are used by both C and Python to insure that no more than
166        # one active thread enters PyMOL at a given time.
167
168        lock_api = pymol.lock_api
169        lock_api_status = pymol.lock_api_status
170        lock_api_glut = pymol.lock_api_glut
171        lock_api_data = pymol.lock_api_data
172        lock_api_allow_flush = 1
173
174        from .locking import *
175        lockcm = LockCM()
176
177        #--------------------------------------------------------------------
178        # status monitoring
179
180        from .monitoring import *
181
182        #--------------------------------------------------------------------
183        # Feedback
184
185        from .feedingback import *
186        from .feedingback import _feedback
187
188        #--------------------------------------------------------------------
189        # internal API routines
190
191        from . import internal
192
193        _alt = internal._alt
194        _coordset_update_spawn = internal._coordset_update_spawn
195        _coordset_update_thread = internal._coordset_update_thread
196        _copy_image = internal._copy_image
197        _ctrl = internal._ctrl
198        _ctsh = internal._ctsh
199        _do = internal._do
200        _dump_floats = internal._dump_floats
201        _dump_ufloats = internal._dump_ufloats
202        _fake_drag = internal._fake_drag
203        _get_color_sc = internal._get_color_sc
204        _get_feedback = internal._get_feedback
205        _interpret_color = internal._interpret_color
206        _invalidate_color_sc = internal._invalidate_color_sc
207        _mpng = internal._mpng
208        _object_update_spawn = internal._object_update_spawn
209        _object_update_thread = internal._object_update_thread
210        _png = internal._png
211        _quit = internal._quit
212        _ray_anti_spawn = internal._ray_anti_spawn
213        _ray_hash_spawn = internal._ray_hash_spawn
214        _ray_spawn = internal._ray_spawn
215        _refresh = internal._refresh
216        _special = internal._special
217        _validate_color_sc = internal._validate_color_sc
218        _cache_get = internal._cache_get
219        _cache_set = internal._cache_set
220        _cache_clear = internal._cache_clear
221        _cache_purge = internal._cache_purge
222        _cache_mark = internal._cache_mark
223        _sdof = internal._sdof
224
225        #######################################################################
226        # now import modules which depend on the above
227        #######################################################################
228
229        from . import editor
230
231        #######################################################################
232        # cmd module functions...
233        #######################################################################
234
235        # for extending the language
236
237        from .commanding import extend, extendaa, alias
238
239        # for documentation etc
240
241        from .helping import python_help
242
243        def write_html_ref(file):
244            '''Write the PyMOL Command Reference to an HTML file'''
245            f=open(file,'w')
246            kees = [a for a in keywords.get_command_keywords()
247                    if not a.startswith('_') and keyword[a][0] != python_help]
248            kees.sort()
249            title = 'PyMOL Command Reference'
250            f.write('''<html>
251<head>
252<title>%s</title>
253<style type='text/css'>
254body, p, h1, h2 {
255  font-family: sans-serif;
256}
257p.api {
258  font:small monospace;
259  color:#999;
260}
261pre.example {
262  background-color: #ccc;
263  padding: 5px;
264}
265li {
266  display: block;
267  width: 10em;
268  float: left;
269}
270</style>
271</head>
272<body>
273<h1>%s</h1>
274
275<p>This is the list of all PyMOL commands which can be used in the PyMOL
276command line and in PML scripts. The command descriptions found in this
277file can also be printed to the PyMOL text buffer with the
278<a href="#help">help</a> command. Example:</p>
279
280<pre class="example">PyMOL&gt;help color
281...</pre>
282
283<p>The list of arguments for a command (the "usage") can be queried on
284the command line with a questionmark. Example:</p>
285
286<pre class="example">PyMOL&gt;color ?
287Usage: color color [, selection [, quiet [, flags ]]]</pre>
288
289<p>The square brackets ("[" and "]") indicate optional arguments and are
290not part of the syntax.</p>
291
292<p>If the PyMOL command interpreter doesn't understand some input, it passes
293it to the Python interpreter. This means that single-line Python expressions
294can be put into PML scripts or typed into the command line. Prefixing a line
295with a slash (/) forces the interpreter to pass it to Python. See also the
296<a href="#python">python</a> command to input multi-line Python scripts.</p>
297
298<p>This file can be generated on the PyMOL command line:</p>
299<pre class="example">PyMOL&gt;cmd.write_html_ref('pymol-command-ref.html')</pre>
300
301<hr size=1>
302
303<ul>
304''' % (title, title))
305
306            for a in kees:
307                f.write("<li><a href='#%s'>%s</a></li>" % (a, a))
308
309            f.write('</ul><br style="clear: both">')
310
311            def make_see_also_link(m):
312                w = m.group()
313                if w in kees:
314                    return "<a href='#%s'>%s</a>" % (w, w)
315                return w
316
317            for a in kees:
318                func = keyword[a][0]
319                doc = (getattr(func, '__doc__') or 'UNDOCUMENTED').strip(). \
320                        replace("<", "&lt;"). \
321                        replace(">", "&gt;").splitlines()
322
323                # attemt to do some HTML formatting
324                isseealso = False
325                for i, line in enumerate(doc):
326                    if not line.strip():
327                        continue
328                    isindented = line[:1].isspace()
329                    if isindented:
330                        if isseealso:
331                            doc[i] = re.sub(r'\w+', make_see_also_link, line)
332                    elif line.isupper():
333                        isseealso = line.startswith('SEE ALSO')
334                        doc[i] = '<b>' + line + '</b>'
335
336                f.write("<hr size=1><h2 id='%s'>%s</h2>" % (a, a))
337                f.write("<pre>%s</pre>" % ('\n'.join(doc)))
338                f.write("<p class='api'>api: %s.%s</p>" % (func.__module__, func.__name__))
339            f.write("</BODY></HTML>")
340            f.close()
341
342            print("PyMOL Command Reference written to %s" % (os.path.abspath(file)))
343
344
345        #####################################################################
346        # Here is where the PyMOL Command Language and API are built.
347        #####################################################################
348
349        # first we need to import a set of symbols into the local namespace
350
351        from .api import *
352
353        # deferred initialization
354
355        _deferred_init_pymol_internals(pymol)
356
357        # now we create the command langauge
358
359        from . import keywords
360        keyword = keywords.get_command_keywords()
361        kw_list = list(keyword.keys())
362
363        keywords.fix_list(kw_list)
364        kwhash = Shortcut(kw_list)
365        keywords.fix_dict(keyword)
366
367        # informational or API-only functions which don't exist in the
368        # PyMOL command language namespace
369
370        help_only = keywords.get_help_only_keywords()
371        help_sc = Shortcut(list(keyword.keys())+list(help_only.keys()))
372
373        # keyboard configuration
374
375        from . import keyboard
376
377        key_mappings = keyboard.get_default_keys()
378
379        selection_sc = lambda sc=Shortcut,gn=get_names:sc(gn('public')+[
380            'not ', 'and ', 'or ', 'first ', 'last ', 'in ', 'like ',
381
382            'byobject ', 'bysegi ', 'bychain ', 'byres ', 'bycalpha ',
383            'bymolecule ',
384
385            'bound_to ', 'neighbor ', 'extend ', 'within ', 'around ',
386            'expand ', 'gap ', 'near_to ', 'beyond ',
387
388            'model ', 'chain ', 'segi ', 'resn ', 'resi ', 'name ', 'alt ', 'index ',
389            'id ', 'rank ',
390
391            'partial_charge ', 'formal_charge ', 'b ', 'q ', 'ss ', 'elem ',
392            'rep ', 'color ', 'pepseq ',
393
394            'all', 'enabled', 'visible', 'bonded', 'protected', 'masked',
395            'organic', 'inorganic', 'solvent', 'polymer', 'guide', 'hetatm',
396            'hydrogens', 'backbone', 'sidechain', 'metals', 'donors',
397            'acceptors',
398            'polymer.protein', 'polymer.nucleic',
399
400            'center', 'origin',
401        ])
402
403        object_sc = lambda sc=Shortcut,gn=get_names:sc(gn('objects'))
404        map_sc = lambda sc=Shortcut,gnot=get_names_of_type:sc(gnot('object:map'))
405        contour_sc =  lambda sc=Shortcut,gnot=get_names_of_type:sc(gnot('object:mesh')+gnot('object:surface'))
406        group_sc = lambda sc=Shortcut,gnot=get_names_of_type:sc(gnot('object:group'))
407
408        # Table for argument autocompletion
409
410        from . import completing
411
412        auto_arg = completing.get_auto_arg_list()
413
414        color_sc = None
415
416        raw_image_callback = None
417