1
2 /*
3 A* -------------------------------------------------------------------
4 B* This file contains source code for the PyMOL computer program
5 C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific.
6 D* -------------------------------------------------------------------
7 E* It is unlawful to modify or remove this copyright notice.
8 F* -------------------------------------------------------------------
9 G* Please see the accompanying LICENSE file for further information.
10 H* -------------------------------------------------------------------
11 I* Additional authors of this source file include:
12 -*
13 -*
14 -*
15 Z* -------------------------------------------------------------------
16 */
17
18
19 /* meaning of defines
20
21 _PYMOL_MONOLITHIC: means that we're building PyMOL and its Python C
22 dependencies as one C library. That means we need to explicitly call
23 the initialization functions for these libraries on startup.
24
25 */
26 #include"os_python.h"
27
28 #include"os_predef.h"
29 #include"os_std.h"
30 #include"Base.h"
31
32
33 /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
34 #ifdef WIN32
35 #include"os_proprietary.h"
36 #include<process.h>
37 #endif
38
39 /* END PROPRIETARY CODE SEGMENT */
40
41 #ifdef _PYMOL_MINGW
42 #define putenv _putenv
43 #endif
44
45 #include"os_time.h"
46 #include"os_unix.h"
47
48 #include"MemoryDebug.h"
49 #include"Base.h"
50 #include"Err.h"
51 #include"P.h"
52 #include"PConv.h"
53 #include"Ortho.h"
54 #include"Cmd.h"
55 #include"main.h"
56 #include"AtomInfo.h"
57 #include"CoordSet.h"
58 #include"Util.h"
59 #include"Executive.h"
60 #include"PyMOLOptions.h"
61 #include"PyMOL.h"
62 #include "Lex.h"
63 #include "Seeker.h"
64
65 #ifdef _PYMOL_IP_PROPERTIES
66 #include"Property.h"
67 #endif
68
69 #include <memory>
70
71 /**
72 * Use with functions which return a PyObject - if they return NULL, then an
73 * unexpected exception has occured.
74 */
75 #define ASSERT_PYOBJECT_NOT_NULL(obj, return_value) \
76 if (!obj) { \
77 PyErr_Print(); \
78 return return_value; \
79 }
80
label_copy_text(char * dst,const char * src,int len,int max)81 static int label_copy_text(char *dst, const char *src, int len, int max)
82 {
83 dst += len;
84 while(len < max) {
85 if(!*src)
86 break;
87 *(dst++) = *(src++);
88 len++;
89 }
90 *dst = 0;
91 return len;
92 }
93
label_next_token(WordType dst,const char ** expr)94 static int label_next_token(WordType dst, const char **expr)
95 {
96 const char *p = *expr;
97 char *q = dst;
98 char ch;
99 int tok_len = 0;
100 int tok_max = sizeof(WordType) - 1;
101
102 /* skip leading whitespace (if any) */
103
104 while((ch = *p)) {
105 if(ch > 33)
106 break;
107 p++;
108 }
109
110 /* copy the token */
111
112 while((ch = *p)) {
113 if(((ch >= 'a') && (ch <= 'z')) ||
114 ((ch >= 'A') && (ch <= 'Z')) || ((ch >= '0') && (ch <= '9')) || ((ch == '_'))) {
115 if(tok_len < tok_max) {
116 *(q++) = ch;
117 tok_len++;
118 }
119 } else {
120 break;
121 }
122 p++;
123 }
124 *q = 0;
125 if(p != *expr)
126 *expr = p;
127 else if(*p)
128 *expr = p + 1; /* always advance input by at least one character */
129
130 /* let caller know whether we read anything */
131
132 return (q != dst);
133 }
134
PLabelExprUsesVariable(PyMOLGlobals * G,const char * expr,const char * var)135 int PLabelExprUsesVariable(PyMOLGlobals * G, const char *expr, const char *var)
136 {
137 char ch, quote = 0;
138 int escaped = false;
139 while((ch = *(expr++))) {
140 if(!quote) {
141 if(ch == '\'') {
142 quote = ch;
143 } else if(ch == '"') {
144 quote = ch;
145 } else if((ch < 33) || (ch == '+') || (ch == '(') || (ch == ')')) {
146 /* nop */
147 } else if(ch > 32) {
148 WordType tok;
149 expr--;
150 if(label_next_token(tok, &expr)) {
151 if(!strcmp(tok, var)) {
152 return 1;
153 }
154 }
155 }
156 } else {
157 if(ch == quote) {
158 quote = 0;
159 } else if(ch == '\\') {
160 if(!escaped) {
161 escaped = true;
162 } else {
163 escaped = false;
164 }
165 }
166 }
167 }
168 return 0;
169 }
170
PLabelAtomAlt(PyMOLGlobals * G,AtomInfoType * at,const char * model,const char * expr,int index)171 int PLabelAtomAlt(PyMOLGlobals * G, AtomInfoType * at, const char *model, const char *expr, int index)
172 {
173 /* alternate C implementation which bypasses Python expressions -- works
174 only for simple label formats "..."+property+... */
175
176 int result = false;
177 OrthoLineType label;
178 int label_len = 0;
179 int label_max = sizeof(OrthoLineType);
180 OrthoLineType buffer;
181 char ch, quote = 0;
182 int escaped = false;
183 const char *origexpr = expr;
184
185 label[0] = 0;
186 while((ch = *(expr++))) {
187 if(!quote) {
188 if(ch == '\'') {
189 quote = ch;
190 } else if(ch == '"') {
191 quote = ch;
192 } else if((ch < 33) || (ch == '+') || (ch == '(') || (ch == ')')) {
193 /* nop */
194 } else if(ch > 32) {
195 WordType tok;
196 int tokresult = true;
197 expr--;
198 if(label_next_token(tok, &expr)) {
199 /* brain-dead linear string matching */
200 buffer[0] = 0;
201 if(!strcmp(tok, "model")) {
202 label_len = label_copy_text(label, model, label_len, label_max);
203 } else if(!strcmp(tok, "index")) {
204 sprintf(buffer, "%d", index + 1);
205 } else if(!strcmp(tok, "type")) {
206 if(at->hetatm)
207 label_len = label_copy_text(label, "HETATM", label_len, label_max);
208 else
209 label_len = label_copy_text(label, "ATOM", label_len, label_max);
210 } else if(!strcmp(tok, "name")) {
211 label_len = label_copy_text(label, LexStr(G, at->name), label_len, label_max);
212 } else if(!strcmp(tok, "resn")) {
213 label_len = label_copy_text(label, LexStr(G, at->resn), label_len, label_max);
214 } else if(!strcmp(tok, "resi")) {
215 sprintf(buffer, "%d%c", at->resv, at->inscode);
216 } else if(!strcmp(tok, "resv")) {
217 sprintf(buffer, "%d", at->resv);
218 } else if(!strcmp(tok, "chain")) {
219 label_len = label_copy_text(label, LexStr(G, at->chain), label_len, label_max);
220 } else if(!strcmp(tok, "alt")) {
221 label_len = label_copy_text(label, at->alt, label_len, label_max);
222 } else if(!strcmp(tok, "segi")) {
223 label_len = label_copy_text(label, LexStr(G, at->segi), label_len, label_max);
224 } else if(!strcmp(tok, "ss")) {
225 label_len = label_copy_text(label, at->ssType, label_len, label_max);
226 } else if(!strcmp(tok, "vdw")) {
227 sprintf(buffer, "%1.2f", at->vdw);
228 } else if(!strcmp(tok, "elec_radius")) {
229 sprintf(buffer, "%1.2f", at->elec_radius);
230 } else if(!strcmp(tok, "text_type")) {
231 const char *st = LexStr(G, at->textType);
232 label_len = label_copy_text(label, st, label_len, label_max);
233 } else if(!strcmp(tok, "custom")) {
234 const char *st = LexStr(G, at->custom);
235 label_len = label_copy_text(label, st, label_len, label_max);
236 } else if(!strcmp(tok, "elem")) {
237 label_len = label_copy_text(label, at->elem, label_len, label_max);
238 } else if(!strcmp(tok, "geom")) {
239 sprintf(buffer, "%d", at->geom);
240 } else if(!strcmp(tok, "valence")) {
241 sprintf(buffer, "%d", at->valence);
242 } else if(!strcmp(tok, "rank")) {
243 sprintf(buffer, "%d", at->rank);
244 } else if(!strcmp(tok, "flags")) {
245 if(at->flags) {
246 sprintf(buffer, "%X", at->flags);
247 } else {
248 strcpy(buffer, "0");
249 }
250 } else if(!strcmp(tok, "q")) {
251 sprintf(buffer, "%1.2f", at->q);
252 } else if(!strcmp(tok, "b")) {
253 sprintf(buffer, "%1.2f", at->b);
254 } else if(!strcmp(tok, "numeric_type")) {
255 if(at->customType != cAtomInfoNoType)
256 sprintf(buffer, "%d", at->customType);
257 else {
258 strcpy(buffer, "?");
259 }
260 } else if(!strcmp(tok, "partial_charge")) {
261 sprintf(buffer, "%1.3f", at->partialCharge);
262 } else if(!strcmp(tok, "formal_charge")) {
263 sprintf(buffer, "%d", at->formalCharge);
264 } else if(!strcmp(tok, "stereo")) {
265 strcpy(buffer, AtomInfoGetStereoAsStr(at));
266 } else if(!strcmp(tok, "color")) {
267 sprintf(buffer, "%d", at->color);
268 } else if(!strcmp(tok, "cartoon")) {
269 sprintf(buffer, "%d", at->cartoon);
270 } else if(!strcmp(tok, "ID")) {
271 sprintf(buffer, "%d", at->id);
272 } else if(!strcmp(tok, "str")) {
273 /* nop */
274 } else {
275 tokresult = false;
276 }
277 if(buffer[0]) {
278 label_len = label_copy_text(label, buffer, label_len, label_max);
279 }
280 } else {
281 if (tok[0]){
282 label_len = label_copy_text(label, "?", label_len, label_max);
283 label_len = label_copy_text(label, tok, label_len, label_max);
284 } else {
285 tokresult = false;
286 }
287 }
288 result |= tokresult;
289 } else {
290 if(label_len < label_max) {
291 label[label_len] = '?';
292 label_len++;
293 result = true;
294 }
295 }
296 } else {
297 if(ch == quote) {
298 quote = 0;
299 result = true;
300 } else if(ch == '\\') {
301 if(!escaped) {
302 escaped = true;
303 } else {
304 if(label_len < label_max) {
305 label[label_len] = ch;
306 label_len++;
307 }
308 escaped = false;
309 }
310 } else {
311 if(label_len < label_max) {
312 label[label_len] = ch;
313 label_len++;
314 label[label_len] = 0;
315 }
316 }
317 }
318 }
319
320 if (!result && !label[0]){
321 // if label is not set, just use expression as a string for label
322 strncpy(label, origexpr, OrthoLineLength);
323 result = true;
324 }
325
326 LexDec(G, at->label);
327 at->label = result ? LexIdx(G, label) : 0;
328
329 return (result);
330 }
331
332 #ifndef _PYMOL_NOPY
333
334
335 /* all of the following Python objects must be invariant & global for the application */
336
337
338 /* these are module / module properties -- global and static for a given interpreter */
339
340
341 /* local to this C code module */
342
343 static PyObject *P_pymol = NULL;
344 static PyObject *P_pymol_dict = NULL; /* must be refomed into globals and instance properties */
345 static PyObject *P_cmd = NULL;
346
347 static PyObject *P_povray = NULL;
348 static PyObject *P_traceback = NULL;
349 static PyObject *P_parser = NULL;
350
351 static PyObject *P_main = NULL;
352 static PyObject *P_vfont = NULL;
353
354 /* module import helper */
355
356 static
PImportModuleOrFatal(const char * name)357 PyObject * PImportModuleOrFatal(const char * name) {
358 PyObject * mod = PyImport_ImportModule(name);
359 if(!mod) {
360 fprintf(stderr, "PyMOL-Error: can't find '%s'\n", name);
361 exit(EXIT_FAILURE);
362 }
363 return mod;
364 }
365
366 static
PGetAttrOrFatal(PyObject * o,const char * name)367 PyObject * PGetAttrOrFatal(PyObject * o, const char * name) {
368 PyObject * attr = PyObject_GetAttrString(o, name);
369 if(!attr) {
370 fprintf(stderr, "PyMOL-Error: can't find '%s'\n", name);
371 exit(EXIT_FAILURE);
372 }
373 return attr;
374 }
375
376 /* used elsewhere */
377
378 PyObject *P_menu = NULL; /* menu definitions are currently global */
379 PyObject *P_xray = NULL; /* okay as global */
380 PyObject *P_chempy = NULL; /* okay as global */
381 PyObject *P_models = NULL; /* okay as global */
382 PyObject *P_setting = NULL; /* okay as global -- just used for names */
383 PyObject *P_CmdException = nullptr;
384 PyObject *P_QuietException = nullptr;
385
386 static PyMappingMethods wrapperMappingMethods, settingMappingMethods;
387 static PyTypeObject Wrapper_Type = {
388 PyVarObject_HEAD_INIT(NULL, 0)
389 "wrapper.Wrapper", /* tp_name */
390 0, /* tp_basicsize */
391 };
392 static PyTypeObject settingWrapper_Type = {
393 PyVarObject_HEAD_INIT(NULL, 0)
394 "wrapper.SettingWrapper", /* tp_name */
395 0, /* tp_basicsize */
396 };
397
398 /*
399 * If `wob` is not in a valid state (outside iterate-family context), raise
400 * an error and return false.
401 */
check_wrapper_scope(WrapperObject * wobj)402 static bool check_wrapper_scope(WrapperObject * wobj) {
403 if (wobj && wobj->obj)
404 return true;
405
406 PyErr_SetString(PyExc_RuntimeError,
407 "wrappers cannot be used outside the iterate-family commands");
408
409 return false;
410 }
411
412 /*
413 * key: Python int (setting index) or str (setting name)
414 *
415 * Return the setting index or -1 for unknown `key`
416 *
417 * Raise LookupError if `key` doesn't name a known setting
418 */
get_and_check_setting_index(PyMOLGlobals * G,PyObject * key)419 static int get_and_check_setting_index(PyMOLGlobals * G, PyObject * key) {
420 int setting_id;
421
422 if(PyInt_Check(key)) {
423 setting_id = PyInt_AS_LONG(key);
424 } else {
425 key = PyObject_Str(key);
426 setting_id = SettingGetIndex(G, PyString_AS_STRING(key));
427 Py_DECREF(key);
428 }
429
430 if (setting_id < 0 || setting_id >= cSetting_INIT) {
431 PyErr_SetString(PyExc_LookupError, "unknown setting");
432 return -1;
433 }
434
435 return setting_id;
436 }
437
438 /*
439 * Access a setting with iterate et. al.
440 *
441 * s[key]
442 *
443 * obj: `s` object in iterate-family namespace
444 *
445 * Raise LookupError if `key` doesn't name a known setting
446 */
447 static
SettingWrapperObjectSubScript(PyObject * obj,PyObject * key)448 PyObject *SettingWrapperObjectSubScript(PyObject *obj, PyObject *key){
449 auto& wobj = reinterpret_cast<SettingPropertyWrapperObject*>(obj)->wobj;
450 int setting_id;
451 PyObject *ret = NULL;
452
453 if (!check_wrapper_scope(wobj)) {
454 return NULL;
455 }
456
457 auto G = wobj->G;
458
459 if ((setting_id = get_and_check_setting_index(G, key)) == -1) {
460 return NULL;
461 }
462
463 if (wobj->idx >= 0){
464 // atom-state level
465 ret = SettingGetIfDefinedPyObject(G, wobj->cs, wobj->idx, setting_id);
466 }
467
468 if (!ret){
469 // atom level
470 ret = SettingGetIfDefinedPyObject(G, wobj->atomInfo, setting_id);
471
472 if (!ret) {
473 // object-state, object, or global
474 ret = SettingGetPyObject(G,
475 wobj->cs ? wobj->cs->Setting : NULL,
476 wobj->obj->Setting, setting_id);
477 }
478 }
479 return PConvAutoNone(ret);
480 }
481
482 /*
483 * Set an atom or atom-state level setting with alter or alter_state.
484 *
485 * s[key] = val
486 *
487 * obj: `s` object in cmd.alter/cmd.alter_state namespace
488 *
489 * Return 0 on success or -1 on failure.
490 *
491 * Raise TypeError if setting not modifiable in the current context, and
492 * LookupError if `key` doesn't name a known setting
493 */
494 static
SettingWrapperObjectAssignSubScript(PyObject * obj,PyObject * key,PyObject * val)495 int SettingWrapperObjectAssignSubScript(PyObject *obj, PyObject *key, PyObject *val){
496 auto& wobj = reinterpret_cast<SettingPropertyWrapperObject*>(obj)->wobj;
497
498 if (!check_wrapper_scope(wobj)) {
499 return -1;
500 }
501
502 int setting_id;
503 auto G = wobj->G;
504
505 if (wobj->read_only){
506 PyErr_SetString(PyExc_TypeError, "Use alter/alter_state to modify settings");
507 return -1;
508 }
509
510 if ((setting_id = get_and_check_setting_index(G, key)) == -1) {
511 return -1;
512 }
513
514 if (wobj->idx >= 0) {
515 // atom-state level
516 if(!SettingLevelCheck(G, setting_id, cSettingLevel_astate)) {
517 PyErr_SetString(PyExc_TypeError,
518 "only atom-state level settings can be set in alter_state function");
519 return -1; // failure
520 } else if (CoordSetSetSettingFromPyObject(G, wobj->cs, wobj->idx, setting_id, val)) {
521 }
522 } else {
523 // atom level
524 if(!SettingLevelCheck(G, setting_id, cSettingLevel_atom)) {
525 PyErr_SetString(PyExc_TypeError,
526 "only atom-level settings can be set in alter function");
527 return -1; // failure
528 } else if (AtomInfoSetSettingFromPyObject(G, wobj->atomInfo, setting_id, val)) {
529 AtomInfoSettingGenerateSideEffects(G, wobj->obj, setting_id, wobj->atm);
530 }
531 }
532
533 return 0; // success
534 }
535
536 /*
537 * Python iterator over atom or atom-state setting indices
538 */
SettingWrapperObjectIter(PyObject * self)539 static PyObject* SettingWrapperObjectIter(PyObject *self)
540 {
541 auto& wobj = reinterpret_cast<SettingPropertyWrapperObject*>(self)->wobj;
542
543 if (!check_wrapper_scope(wobj)) {
544 return NULL;
545 }
546
547 int unique_id = wobj->atomInfo->unique_id;
548
549 if (wobj->idx >= 0) {
550 unique_id =
551 wobj->cs->atom_state_setting_id ?
552 wobj->cs->atom_state_setting_id[wobj->idx] : 0;
553 }
554
555 PyObject * items = SettingUniqueGetIndicesAsPyList(wobj->G, unique_id);
556 PyObject * iter = PyObject_GetIter(items);
557 Py_XDECREF(items);
558
559 return iter;
560 }
561
562 /*
563 * Allows attribute-like syntax for item lookups
564 *
565 * o.key -> o[key] if `key` is not an attribute of `o`
566 */
PyObject_GenericGetAttrOrItem(PyObject * o,PyObject * key)567 static PyObject* PyObject_GenericGetAttrOrItem(PyObject *o, PyObject *key) {
568 PyObject *ret = PyObject_GenericGetAttr(o, key);
569 if (!PyErr_Occurred())
570 return ret;
571 PyErr_Clear();
572 return PyObject_GetItem(o, key);
573 }
574
575 /*
576 * Allows attribute-like syntax for item assignment
577 *
578 * `o.key = value` -> `o[key] = value`
579 */
580 static
PyObject_GenericSetAttrAsItem(PyObject * o,PyObject * key,PyObject * value)581 int PyObject_GenericSetAttrAsItem(PyObject *o, PyObject *key, PyObject *value) {
582 return PyObject_SetItem(o, key, value);
583 }
584
585 /*
586 * iterate-family namespace implementation: lookup
587 *
588 * Raise NameError if state attributes are accessed outside of iterate_state
589 */
590 static
WrapperObjectSubScript(PyObject * obj,PyObject * key)591 PyObject * WrapperObjectSubScript(PyObject *obj, PyObject *key){
592
593 static PyObject * pystr_HETATM = PyString_InternFromString("HETATM");
594 static PyObject * pystr_ATOM = PyString_InternFromString("ATOM");
595 static PyObject * pystr_QuestionMark = PyString_InternFromString("?");
596
597 WrapperObject *wobj = (WrapperObject*)obj;
598
599 if (!check_wrapper_scope(wobj))
600 return NULL;
601
602 PyMOLGlobals * G = wobj->G;
603 const char *aprop;
604 AtomPropertyInfo *ap;
605 PyObject *ret = NULL;
606 bool borrowed = false;
607 PyObject *keyobj = PyObject_Str(key);
608 aprop = PyString_AS_STRING(keyobj);
609 ap = PyMOL_GetAtomPropertyInfo(wobj->G->PyMOL, aprop);
610 Py_DECREF(keyobj);
611 if (ap){
612 #ifdef _PYMOL_IP_EXTRAS
613 #ifdef NO_MMLIBS
614 // static -> only show these warnings once
615 static bool warning_shown_text_type = false;
616 #endif
617
618 switch (ap->id) {
619 case ATOM_PROP_STEREO:
620 if (ObjectMoleculeUpdateMMStereoInfoForState(G, wobj->obj, wobj->state - 1) < 0) {
621 PyErr_SetString(PyExc_RuntimeError,
622 "please install rdkit or set SCHRODINGER variable");
623 return NULL;
624 }
625 break;
626 case ATOM_PROP_TEXT_TYPE:
627 #ifndef NO_MMLIBS
628 ObjectMoleculeUpdateAtomTypeInfoForState(G, wobj->obj, wobj->state - 1, 1, 0);
629 #else
630 if (!warning_shown_text_type && !(wobj->cs && wobj->cs->validTextType)) {
631 warning_shown_text_type = true;
632 PRINTFB(G, FB_ObjectMolecule, FB_Warnings)
633 " Notice: Automatic 'text_type' assignment feature removed in PyMOL 2.0.\n" ENDFB(G);
634 }
635 #endif
636 break;
637 }
638 #endif
639
640 switch (ap->Ptype){
641 case cPType_string:
642 {
643 char *val = (char*)(((char*)wobj->atomInfo) + ap->offset);
644 ret = PyString_FromString(val);
645 }
646 break;
647 case cPType_schar:
648 {
649 signed char val = *(signed char*)(((char*)wobj->atomInfo) + ap->offset);
650 ret = PyInt_FromLong((long)val);
651 }
652 break;
653 case cPType_int:
654 {
655 int val = *(int*)(((char*)wobj->atomInfo) + ap->offset);
656 ret = PyInt_FromLong((long)val);
657 }
658 break;
659 case cPType_int_as_string:
660 {
661 const char *st = LexStr(wobj->G,
662 *reinterpret_cast<lexidx_t*>
663 (((char*)wobj->atomInfo) + ap->offset));
664 ret = PyString_FromString(st);
665 }
666 break;
667 case cPType_float:
668 {
669 float val = *(float*)(((char*)wobj->atomInfo) + ap->offset);
670 ret = PyFloat_FromDouble(val);
671 }
672 break;
673 case cPType_char_as_type:
674 {
675 ret = wobj->atomInfo->hetatm ? pystr_HETATM : pystr_ATOM;
676 borrowed = true;
677 }
678 break;
679 case cPType_model:
680 ret = PyString_FromString(wobj->obj->Name);
681 break;
682 case cPType_index:
683 {
684 ret = PyInt_FromLong((long)wobj->atm + 1);
685 }
686 break;
687 case cPType_int_custom_type:
688 {
689 int val = *(int*)(((char*)wobj->atomInfo) + ap->offset);
690 if(val != cAtomInfoNoType){
691 ret = PyInt_FromLong((long)val);
692 } else {
693 ret = pystr_QuestionMark;
694 borrowed = true;
695 }
696 }
697 break;
698 case cPType_xyz_float:
699 {
700 if (wobj->idx >= 0){
701 ret = PyFloat_FromDouble(wobj->cs->coordPtr(wobj->idx)[ap->offset]);
702 } else {
703 PyErr_SetString(PyExc_NameError,
704 "x/y/z only available in iterate_state and alter_state");
705 }
706 }
707 break;
708 case cPType_settings:
709 if (!wobj->settingWrapperObject) {
710 wobj->settingWrapperObject = PyType_GenericNew(&settingWrapper_Type, Py_None, Py_None);
711 reinterpret_cast<SettingPropertyWrapperObject *>(wobj->settingWrapperObject)->wobj = wobj;
712 }
713 ret = wobj->settingWrapperObject;
714 borrowed = true;
715 break;
716 case cPType_properties:
717 PyErr_SetString(PyExc_NotImplementedError,
718 "'properties/p' not supported in Open-Source PyMOL");
719 break;
720 case cPType_state:
721 ret = PyInt_FromLong((long)wobj->state);
722 break;
723 default:
724 switch (ap->id) {
725 case ATOM_PROP_RESI:
726 {
727 char resi[8];
728 AtomResiFromResv(resi, sizeof(resi), wobj->atomInfo);
729 ret = PyString_FromString(resi);
730 }
731 break;
732 case ATOM_PROP_STEREO:
733 {
734 auto mmstereotype = AtomInfoGetStereoAsStr(wobj->atomInfo);
735 ret = PyString_FromString(mmstereotype);
736 }
737 break;
738 case ATOM_PROP_ONELETTER:
739 {
740 const char * st = LexStr(G, wobj->atomInfo->resn);
741 char abbr[2] = {SeekerGetAbbr(G, st, 'O', 'X'), 0};
742 ret = PyString_FromString(abbr);
743 }
744 break;
745 default:
746 PyErr_SetString(PyExc_SystemError, "unhandled atom property type");
747 }
748 }
749 } else {
750 /* if not an atom property, check if local variable in dict */
751 if (wobj->dict) {
752 ret = PyDict_GetItem(wobj->dict, key);
753 }
754 if (ret) {
755 borrowed = true;
756 } else {
757 PyErr_SetNone(PyExc_KeyError);
758 }
759 }
760
761 if (borrowed)
762 PXIncRef(ret);
763 return ret;
764 }
765
766 /*
767 * iterate-family namespace implementation: assignment
768 *
769 * Raise TypeError for read-only variables
770 */
771 static
WrapperObjectAssignSubScript(PyObject * obj,PyObject * key,PyObject * val)772 int WrapperObjectAssignSubScript(PyObject *obj, PyObject *key, PyObject *val){
773 WrapperObject *wobj = (WrapperObject*)obj;
774
775 if (!check_wrapper_scope(wobj)) {
776 return -1;
777 }
778 {
779 char aprop[16];
780 PyObject *keyobj = PyObject_Str(key);
781 UtilNCopy(aprop, PyString_AS_STRING(keyobj), sizeof(aprop));
782 Py_DECREF(keyobj);
783
784 AtomPropertyInfo *ap = PyMOL_GetAtomPropertyInfo(wobj->G->PyMOL, aprop);
785
786 if (ap){
787 #ifdef _PYMOL_IP_EXTRAS
788 if (wobj->cs) {
789 switch (ap->id) {
790 case ATOM_PROP_STEREO:
791 wobj->cs->validMMStereo = MMPYMOLX_PROP_STATE_USER;
792 break;
793 case ATOM_PROP_TEXT_TYPE:
794 wobj->cs->validTextType = MMPYMOLX_PROP_STATE_USER;
795 break;
796 }
797 }
798 #endif
799
800 short changed = false;
801 if (wobj->read_only){
802 PyErr_SetString(PyExc_TypeError,
803 "Use alter/alter_state to modify values");
804 return -1;
805 }
806
807 // alter_state: must be setting x/y/z or flags
808 if (wobj->idx >= 0) {
809 if (ap->Ptype == cPType_xyz_float) {
810 float * v = wobj->cs->coordPtr(wobj->idx) + ap->offset;
811 PConvPyObjectToFloat(val, v);
812 return 0;
813 }
814 }
815
816 switch (ap->Ptype){
817 case cPType_string:
818 {
819 PyObject *valobj = PyObject_Str(val);
820 const char *valstr = PyString_AS_STRING(valobj);
821 char *dest = (char*)(((char*)wobj->atomInfo) + ap->offset);
822 if (strlen(valstr) > ap->maxlen){
823 strncpy(dest, valstr, ap->maxlen);
824 } else {
825 strcpy(dest, valstr);
826 }
827 Py_DECREF(valobj);
828 changed = true;
829 }
830 break;
831 case cPType_schar:
832 {
833 int valint = PyInt_AsLong(val);
834 signed char *dest;
835 if (valint == -1 && PyErr_Occurred())
836 break;
837 dest = (signed char*)(((char*)wobj->atomInfo) + ap->offset);
838 *dest = valint;
839 changed = true;
840 }
841 break;
842 case cPType_int:
843 {
844 int valint = PyInt_AsLong(val);
845 int *dest;
846 if (valint == -1 && PyErr_Occurred())
847 break;
848 dest = (int*)(((char*)wobj->atomInfo) + ap->offset);
849 *dest = valint;
850 changed = true;
851 }
852 break;
853 case cPType_int_as_string:
854 {
855 auto dest = reinterpret_cast<lexidx_t*>
856 (((char*)wobj->atomInfo) + ap->offset);
857 PyObject *valobj = PyObject_Str(val);
858 const char *valstr = PyString_AS_STRING(valobj);
859 LexDec(wobj->G, *dest);
860 *dest = LexIdx(wobj->G, valstr);
861 Py_DECREF(valobj);
862 changed = true;
863 }
864 break;
865 case cPType_float:
866 {
867 float *dest = (float*)(((char*)wobj->atomInfo) + ap->offset);
868 changed = PConvPyObjectToFloat(val, dest);
869 }
870 break;
871 case cPType_char_as_type:
872 {
873 PyObject *valobj = PyObject_Str(val);
874 const char *valstr = PyString_AS_STRING(valobj);
875 wobj->atomInfo->hetatm = ((valstr[0] == 'h') || (valstr[0] == 'H'));
876 Py_DECREF(valobj);
877 changed = true;
878 }
879 break;
880 case cPType_int_custom_type:
881 {
882 PyObject *valobj = PyObject_Str(val);
883 const char *valstr = PyString_AS_STRING(valobj);
884 int *dest = (int*)(((char*)wobj->atomInfo) + ap->offset);
885 if (valstr[0] == '?'){
886 *dest = cAtomInfoNoType;
887 } else {
888 int valint = PyInt_AS_LONG(val);
889 *dest = valint;
890 }
891 Py_DECREF(valobj);
892 changed = true;
893 }
894 break;
895 case cPType_xyz_float:
896 PyErr_SetString(PyExc_NameError,
897 "x/y/z only available in alter_state");
898 return -1;
899 default:
900 switch (ap->id) {
901 case ATOM_PROP_RESI:
902 if (PConvPyIntToInt(val, &wobj->atomInfo->resv)) {
903 wobj->atomInfo->inscode = '\0';
904 } else {
905 PyObject *valobj = PyObject_Str(val);
906 wobj->atomInfo->setResi(PyString_AS_STRING(valobj));
907 Py_DECREF(valobj);
908 }
909 break;
910 case ATOM_PROP_STEREO:
911 {
912 PyObject *valobj = PyObject_Str(val);
913 const char *valstr = PyString_AS_STRING(valobj);
914 AtomInfoSetStereo(wobj->atomInfo, valstr);
915 Py_DECREF(valobj);
916 }
917 break;
918 default:
919 PyErr_Format(PyExc_TypeError, "'%s' is read-only", aprop);
920 return -1;
921 }
922 }
923 if (changed){
924 switch (ap->id){
925 case ATOM_PROP_ELEM:
926 wobj->atomInfo->protons = 0;
927 wobj->atomInfo->vdw = 0;
928 AtomInfoAssignParameters(wobj->G, wobj->atomInfo);
929 break;
930 case ATOM_PROP_RESV:
931 wobj->atomInfo->inscode = '\0';
932 break;
933 case ATOM_PROP_SS:
934 wobj->atomInfo->ssType[0] = toupper(wobj->atomInfo->ssType[0]);
935 break;
936 case ATOM_PROP_FORMAL_CHARGE:
937 wobj->atomInfo->chemFlag = false;
938 break;
939 }
940 }
941 } else {
942 /* if not an atom property, then its a local variable, store it */
943 if (!wobj->dict) {
944 wobj->dict = PyDict_New();
945 }
946 PyDict_SetItem(wobj->dict, key, val);
947 }
948 }
949 return 0; /* 0 success, -1 failure */
950 }
951
952 /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
953 #ifdef WIN32
954 static PyObject *P_time = NULL;
955 static PyObject *P_sleep = NULL;
956 #endif
957
958 /* END PROPRIETARY CODE SEGMENT */
959
960 static void PUnlockAPIWhileBlocked(PyMOLGlobals * G);
961 static void PLockAPIWhileBlocked(PyMOLGlobals * G);
962
963 #define P_log_file_str "_log_file"
964
965 /**
966 * Acquire the status lock (blocking)
967 * @pre GIL
968 */
PLockStatus(PyMOLGlobals * G)969 void PLockStatus(PyMOLGlobals * G)
970 { /* assumes we have the GIL */
971 PXDecRef(PYOBJECT_CALLMETHOD(G->P_inst->lock_api_status, "acquire", nullptr));
972 }
973
974 /**
975 * Acquire the status lock (non-blocking)
976 * @return True if lock could be acquired
977 * @pre GIL
978 */
PLockStatusAttempt(PyMOLGlobals * G)979 int PLockStatusAttempt(PyMOLGlobals * G)
980 { /* assumes we have the GIL */
981
982 unique_PyObject_ptr got_lock(
983 PYOBJECT_CALLMETHOD(G->P_inst->lock_api_status, "acquire", "i", 0));
984
985 ASSERT_PYOBJECT_NOT_NULL(got_lock, true);
986
987 return PyObject_IsTrue(got_lock.get());
988 }
989
990 /**
991 * Release the status lock
992 * @pre GIL
993 */
PUnlockStatus(PyMOLGlobals * G)994 void PUnlockStatus(PyMOLGlobals * G)
995 { /* assumes we have the GIL */
996 PXDecRef(PYOBJECT_CALLMETHOD(G->P_inst->lock_api_status, "release", nullptr));
997 }
998
999 /**
1000 * Acquire GLUT lock
1001 * @pre GIL
1002 */
PLockGLUT(PyMOLGlobals * G)1003 static void PLockGLUT(PyMOLGlobals * G)
1004 { /* assumes we have the GIL */
1005 PXDecRef(PYOBJECT_CALLMETHOD(G->P_inst->lock_api_glut, "acquire", nullptr));
1006 }
1007
1008 /**
1009 * Release GLUT lock
1010 * @pre GIL
1011 */
PUnlockGLUT(PyMOLGlobals * G)1012 static void PUnlockGLUT(PyMOLGlobals * G)
1013 { /* assumes we have the GIL */
1014 PXDecRef(PYOBJECT_CALLMETHOD(G->P_inst->lock_api_glut, "release", nullptr));
1015 }
1016
1017 static long P_glut_thread_id = -1;
1018
1019
1020 /* enables us to keep glut out if by chance it grabs the API
1021 * in the middle of a nested API based operation */
1022
1023 static
1024 void PCatchInit(void);
1025
1026
1027 /*
1028 PyObject *GetBondsDict(PyMOLGlobals *G)
1029 {
1030 PyObject *result = NULL;
1031 result = PyObject_GetAttrString(P_chempy,"bonds");
1032 if(!result) ErrMessage(G,"PyMOL","can't find 'chempy.bonds.bonds'");
1033 return(result);
1034 }
1035 */
1036
PGetFontDict(PyMOLGlobals * G,float size,int face,int style)1037 PyObject *PGetFontDict(PyMOLGlobals * G, float size, int face, int style)
1038 { /* assumes we have a valid interpreter lock */
1039 PyObject *result = NULL;
1040
1041 if(!P_vfont) {
1042 P_vfont = PyImport_ImportModule("pymol.vfont");
1043 }
1044 if(!P_vfont) {
1045 PRINTFB(G, FB_Python, FB_Errors)
1046 " PyMOL-Error: can't find module 'vfont'" ENDFB(G);
1047 } else {
1048 result = PYOBJECT_CALLMETHOD(P_vfont, "get_font", "fii", size, face, style);
1049 }
1050 return (PConvAutoNone(result));
1051 }
1052
PComplete(PyMOLGlobals * G,char * str,int buf_size)1053 int PComplete(PyMOLGlobals * G, char *str, int buf_size)
1054 {
1055 int ret = false;
1056 PyObject *result;
1057 const char *st2;
1058 PBlockAndUnlockAPI(G);
1059 if(G->P_inst->complete) {
1060 result = PYOBJECT_CALLFUNCTION(G->P_inst->complete, "s", str);
1061 if(result) {
1062 if(PyString_Check(result)) {
1063 st2 = PyString_AsString(result);
1064 UtilNCopy(str, st2, buf_size);
1065 ret = true;
1066 }
1067 Py_DECREF(result);
1068 }
1069 }
1070 PLockAPIAndUnblock(G);
1071 return (ret);
1072 }
1073
PTruthCallStr0(PyObject * object,const char * method)1074 int PTruthCallStr0(PyObject * object, const char *method)
1075 {
1076 int result = false;
1077 PyObject *tmp;
1078 tmp = PYOBJECT_CALLMETHOD(object, method, "");
1079 if(tmp) {
1080 if(PyObject_IsTrue(tmp))
1081 result = 1;
1082 Py_DECREF(tmp);
1083 }
1084 return (result);
1085 }
1086
PTruthCallStr(PyObject * object,const char * method,const char * argument)1087 int PTruthCallStr(PyObject * object, const char *method, const char *argument)
1088 {
1089 int result = false;
1090 PyObject *tmp;
1091 tmp = PYOBJECT_CALLMETHOD(object, method, "s", argument);
1092 if(tmp) {
1093 if(PyObject_IsTrue(tmp))
1094 result = 1;
1095 Py_DECREF(tmp);
1096 }
1097 return (result);
1098 }
1099
PTruthCallStr1i(PyObject * object,const char * method,int argument)1100 int PTruthCallStr1i(PyObject * object, const char *method, int argument)
1101 {
1102 int result = false;
1103 PyObject *tmp;
1104 tmp = PYOBJECT_CALLMETHOD(object, method, "i", argument);
1105 if(tmp) {
1106 if(PyObject_IsTrue(tmp))
1107 result = 1;
1108 Py_DECREF(tmp);
1109 }
1110 return (result);
1111 }
1112
PTruthCallStr1s(PyObject * object,const char * method,const char * argument)1113 int PTruthCallStr1s(PyObject * object, const char *method, const char *argument)
1114 {
1115 int result = false;
1116 PyObject *tmp;
1117 tmp = PYOBJECT_CALLMETHOD(object, method, "s", argument);
1118 if(tmp) {
1119 if(PyObject_IsTrue(tmp))
1120 result = 1;
1121 Py_DECREF(tmp);
1122 }
1123 return (result);
1124 }
1125
PTruthCallStr4i(PyObject * object,const char * method,int a1,int a2,int a3,int a4)1126 int PTruthCallStr4i(PyObject * object, const char *method, int a1, int a2, int a3, int a4)
1127 {
1128 int result = false;
1129 PyObject *tmp;
1130 tmp = PYOBJECT_CALLMETHOD(object, method, "iiii", a1, a2, a3, a4);
1131 if(tmp) {
1132 if(PyObject_IsTrue(tmp))
1133 result = 1;
1134 Py_DECREF(tmp);
1135 }
1136 return (result);
1137 }
1138
PXIncRef(PyObject * obj)1139 PyObject *PXIncRef(PyObject * obj)
1140 {
1141 if(!obj)
1142 obj = Py_None;
1143 Py_XINCREF(obj);
1144 return obj;
1145 }
1146
PXDecRef(PyObject * obj)1147 void PXDecRef(PyObject * obj)
1148 {
1149 Py_XDECREF(obj);
1150 }
1151
CacheCreateEntry(PyObject ** result,PyObject * input)1152 static ov_status CacheCreateEntry(PyObject ** result, PyObject * input)
1153 {
1154 ov_status status = OV_STATUS_FAILURE;
1155 if(input && PyTuple_Check(input)) {
1156 ov_size tuple_size = PyTuple_Size(input);
1157 ov_size tot_size = tuple_size;
1158 PyObject *hash_code = PyTuple_New(tuple_size);
1159 PyObject *entry = PyList_New(6);
1160 if(hash_code && entry) {
1161 /* compute hash codes & total input size */
1162 ov_size i;
1163 status = OV_STATUS_SUCCESS;
1164 for(i = 0; i < tuple_size; i++) {
1165 PyObject *item = PyTuple_GetItem(input, i);
1166 long hash_long;
1167 if(item != Py_None) {
1168 /* here we are assuming that different Python versions will
1169 * hash tuples of ints & floats in exactly the same way (at least to 31 bits of significance) */
1170 hash_long = 0x7FFFFFFF & PyObject_Hash(item); /* pos 32 bit # to preserve 32-bit/64-bit compat */
1171 } else {
1172 hash_long = 0; /* None doesn't hash consistently from Python version to version */
1173 }
1174 PyTuple_SetItem(hash_code, i, PyInt_FromLong(hash_long));
1175 if(PyTuple_Check(item)) {
1176 tot_size += PyTuple_Size(item);
1177 }
1178 }
1179 PyList_SetItem(entry, 0, PyInt_FromLong(tot_size));
1180 PyList_SetItem(entry, 1, hash_code);
1181 PyList_SetItem(entry, 2, PXIncRef(input));
1182 PyList_SetItem(entry, 3, PXIncRef(NULL));
1183 PyList_SetItem(entry, 4, PyInt_FromLong(0)); /* access count */
1184 PyList_SetItem(entry, 5, PyFloat_FromDouble(0.0)); /* timestamp */
1185 }
1186 if(!OV_OK(status)) {
1187 PXDecRef(hash_code);
1188 PXDecRef(entry);
1189 } else {
1190 *result = entry;
1191 }
1192 }
1193 if(PyErr_Occurred())
1194 PyErr_Print();
1195 return status;
1196 }
1197
PCacheSet(PyMOLGlobals * G,PyObject * entry,PyObject * output)1198 ov_status PCacheSet(PyMOLGlobals * G, PyObject * entry, PyObject * output)
1199 {
1200 ov_status status = OV_STATUS_FAILURE;
1201 if(G->P_inst->cache && output) {
1202 ov_size tuple_size = PyTuple_Size(output);
1203 ov_size tot_size = tuple_size + PyInt_AsLong(PyList_GetItem(entry, 0));
1204 status = OV_STATUS_SUCCESS;
1205 {
1206 ov_size i;
1207 for(i = 0; i < tuple_size; i++) {
1208 PyObject *item = PyTuple_GetItem(output, i);
1209 if(PyTuple_Check(item)) {
1210 tot_size += PyTuple_Size(item);
1211 }
1212 }
1213 }
1214 PyList_SetItem(entry, 0, PyInt_FromLong(tot_size)); /* update total size */
1215 PyList_SetItem(entry, 3, PXIncRef(output));
1216 PXDecRef(PYOBJECT_CALLMETHOD(G->P_inst->cmd, "_cache_set",
1217 "OiO", entry, SettingGetGlobal_i(G, cSetting_cache_max),
1218 G->P_inst->cmd));
1219 /* compute the hash codes */
1220 }
1221 if(PyErr_Occurred())
1222 PyErr_Print();
1223 return status;
1224 }
1225
PCacheGet(PyMOLGlobals * G,PyObject ** result_output,PyObject ** result_entry,PyObject * input)1226 ov_status PCacheGet(PyMOLGlobals * G,
1227 PyObject ** result_output, PyObject ** result_entry, PyObject * input)
1228 {
1229 ov_status status = OV_STATUS_NO;
1230 if(G->P_inst->cache) {
1231 PyObject *entry = NULL;
1232 PyObject *output = NULL;
1233
1234 if(OV_OK(CacheCreateEntry(&entry, input))) {
1235 output = PYOBJECT_CALLMETHOD(G->P_inst->cmd, "_cache_get",
1236 "OOO", entry, Py_None, G->P_inst->cmd);
1237 if(output == Py_None) {
1238 Py_DECREF(output);
1239 output = NULL;
1240 } else {
1241 status = OV_STATUS_YES;
1242 }
1243 }
1244 /* compute the hash codes */
1245 if(OV_OK(status)) {
1246 *result_entry = entry;
1247 *result_output = output;
1248 } else {
1249 PXDecRef(entry);
1250 PXDecRef(output);
1251 }
1252 }
1253 if(PyErr_Occurred())
1254 PyErr_Print();
1255 return status;
1256 }
1257
PSleepWhileBusy(PyMOLGlobals * G,int usec)1258 void PSleepWhileBusy(PyMOLGlobals * G, int usec)
1259 {
1260 #ifndef WIN32
1261 struct timeval tv;
1262 PRINTFD(G, FB_Threads)
1263 " PSleep-DEBUG: napping.\n" ENDFD;
1264 tv.tv_sec = 0;
1265 tv.tv_usec = usec;
1266 select(0, NULL, NULL, NULL, &tv);
1267 PRINTFD(G, FB_Threads)
1268 " PSleep-DEBUG: nap over.\n" ENDFD;
1269 #else
1270 /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
1271 PBlock(G);
1272 PXDecRef(PYOBJECT_CALLFUNCTION(P_sleep, "f", usec / 1000000.0));
1273 PUnblock(G);
1274 /* END PROPRIETARY CODE SEGMENT */
1275 #endif
1276 }
1277
PSleepUnlocked(PyMOLGlobals * G,int usec)1278 void PSleepUnlocked(PyMOLGlobals * G, int usec)
1279 { /* can only be called by the glut process */
1280 #ifndef WIN32
1281 struct timeval tv;
1282 PRINTFD(G, FB_Threads)
1283 " PSleep-DEBUG: napping.\n" ENDFD;
1284 tv.tv_sec = 0;
1285 tv.tv_usec = usec;
1286 select(0, NULL, NULL, NULL, &tv);
1287 PRINTFD(G, FB_Threads)
1288 " PSleep-DEBUG: nap over.\n" ENDFD;
1289 #else
1290 /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
1291 PBlock(G);
1292 PXDecRef(PYOBJECT_CALLFUNCTION(P_sleep, "f", usec / 1000000.0));
1293 PUnblock(G);
1294 /* END PROPRIETARY CODE SEGMENT */
1295 #endif
1296 }
1297
PSleep(PyMOLGlobals * G,int usec)1298 void PSleep(PyMOLGlobals * G, int usec)
1299 { /* can only be called by the glut process */
1300 #ifndef WIN32
1301 struct timeval tv;
1302 PUnlockAPIAsGlut(G);
1303 PRINTFD(G, FB_Threads)
1304 " PSleep-DEBUG: napping.\n" ENDFD;
1305 tv.tv_sec = 0;
1306 tv.tv_usec = usec;
1307 select(0, NULL, NULL, NULL, &tv);
1308 PRINTFD(G, FB_Threads)
1309 " PSleep-DEBUG: nap over.\n" ENDFD;
1310 PLockAPIAsGlut(G, true);
1311 #else
1312 /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
1313 PBlockAndUnlockAPI(G);
1314 PXDecRef(PYOBJECT_CALLFUNCTION(P_sleep, "f", usec / 1000000.0));
1315 PLockAPIAndUnblock(G);
1316 /* END PROPRIETARY CODE SEGMENT */
1317 #endif
1318
1319 }
1320
1321 static PyObject *PCatchWrite(PyObject * self, PyObject * args);
1322 static PyObject *PCatch_install(PyObject * self, PyObject * args);
1323
1324 static
my_interrupt(int a)1325 void my_interrupt(int a)
1326 {
1327 exit(EXIT_FAILURE);
1328 }
1329
PDumpTraceback(PyObject * err)1330 void PDumpTraceback(PyObject * err)
1331 {
1332 PYOBJECT_CALLMETHOD(P_traceback, "print_tb", "O", err);
1333 }
1334
PDumpException()1335 void PDumpException()
1336 {
1337 PYOBJECT_CALLMETHOD(P_traceback, "print_exc", "");
1338 }
1339
1340 static
WrapperObjectNew()1341 WrapperObject * WrapperObjectNew() {
1342 auto wobj = (WrapperObject *)PyType_GenericNew(&Wrapper_Type, Py_None, Py_None);
1343 wobj->dict = NULL;
1344 wobj->settingWrapperObject = NULL;
1345 #ifdef _PYMOL_IP_PROPERTIES
1346 wobj->propertyWrapperObject = NULL;
1347 #endif
1348 return wobj;
1349 }
1350
PAlterAtomState(PyMOLGlobals * G,PyCodeObject * expr_co,int read_only,ObjectMolecule * obj,CoordSet * cs,int atm,int idx,int state,PyObject * space)1351 int PAlterAtomState(PyMOLGlobals * G, PyCodeObject *expr_co, int read_only,
1352 ObjectMolecule *obj, CoordSet *cs, int atm, int idx,
1353 int state, PyObject * space)
1354
1355 /* assumes Blocked python interpreter */
1356 {
1357 int result = true;
1358
1359 auto wobj = WrapperObjectNew();
1360 wobj->G = G;
1361 wobj->obj = obj;
1362 wobj->cs = cs;
1363 wobj->atomInfo = obj->AtomInfo + atm;
1364 wobj->atm = atm;
1365 wobj->idx = idx;
1366 wobj->read_only = read_only;
1367 wobj->state = state + 1;
1368
1369 PXDecRef(PyEval_EvalCode(expr_co, space, (PyObject*)wobj));
1370 WrapperObjectReset(wobj);
1371
1372 if(PyErr_Occurred()) {
1373 PyErr_Print();
1374 result = false;
1375 }
1376 return result;
1377 }
1378
PAlterAtom(PyMOLGlobals * G,ObjectMolecule * obj,CoordSet * cs,PyCodeObject * expr_co,int read_only,int atm,PyObject * space)1379 int PAlterAtom(PyMOLGlobals * G,
1380 ObjectMolecule *obj, CoordSet *cs, PyCodeObject *expr_co, int read_only,
1381 int atm, PyObject * space)
1382 {
1383 int state = (obj->DiscreteFlag ? obj->AtomInfo[atm].discrete_state : 0) - 1;
1384 return PAlterAtomState(G, expr_co, read_only, obj, cs, atm, /* idx */ -1, state, space);
1385 }
1386
1387 /*
1388 * String conversion which takes "label_digits" setting into account.
1389 */
1390 static
PLabelPyObjectToStrMaxLen(PyMOLGlobals * G,PyObject * obj,char * buffer,int maxlen)1391 int PLabelPyObjectToStrMaxLen(PyMOLGlobals * G, PyObject * obj, char *buffer, int maxlen)
1392 {
1393 if (obj && PyFloat_Check(obj)) {
1394 snprintf(buffer, maxlen + 1, "%.*f",
1395 SettingGetGlobal_i(G, cSetting_label_digits),
1396 PyFloat_AsDouble(obj));
1397 return true;
1398 }
1399 return PConvPyObjectToStrMaxLen(obj, buffer, maxlen);
1400 }
1401
PLabelAtom(PyMOLGlobals * G,ObjectMolecule * obj,CoordSet * cs,PyCodeObject * expr_co,int atm)1402 int PLabelAtom(PyMOLGlobals * G, ObjectMolecule *obj, CoordSet *cs, PyCodeObject *expr_co, int atm)
1403 {
1404 int result = true;
1405 PyObject *P_inst_dict = G->P_inst->dict;
1406 PyObject *resultPyObject;
1407 OrthoLineType label;
1408 AtomInfoType * ai = obj->AtomInfo + atm;
1409
1410 if (!expr_co){
1411 // unsetting label
1412 LexAssign(G, ai->label, 0);
1413 return true;
1414 }
1415
1416 auto wobj = WrapperObjectNew();
1417 wobj->G = G;
1418 wobj->obj = obj;
1419 wobj->cs = cs;
1420 wobj->atomInfo = ai;
1421 wobj->atm = atm;
1422 wobj->idx = -1;
1423 wobj->read_only = true;
1424
1425 if (obj->DiscreteFlag) {
1426 wobj->state = obj->AtomInfo[atm].discrete_state;
1427 } else {
1428 wobj->state = 0;
1429 }
1430
1431 resultPyObject = PyEval_EvalCode(expr_co, P_inst_dict, (PyObject*)wobj);
1432 WrapperObjectReset(wobj);
1433
1434 if(PyErr_Occurred()) {
1435 PyErr_Print();
1436 result = false;
1437 } else {
1438 result = true;
1439 if(!PLabelPyObjectToStrMaxLen(G, resultPyObject,
1440 label, sizeof(OrthoLineType) - 1))
1441 result = false;
1442 if(PyErr_Occurred()) {
1443 PyErr_Print();
1444 result = false;
1445 }
1446 if(result) {
1447 LexAssign(G, ai->label, label);
1448 } else {
1449 ErrMessage(G, "Label", "Aborting on error. Labels may be incomplete.");
1450 }
1451 }
1452 PXDecRef(resultPyObject);
1453 return (result);
1454 }
1455
1456 /**
1457 * - Release API lock with flushing
1458 * - Pop valid context
1459 * - Release GLUT lock
1460 * @pre no GIL
1461 */
PUnlockAPIAsGlut(PyMOLGlobals * G)1462 void PUnlockAPIAsGlut(PyMOLGlobals * G)
1463 { /* must call with unblocked interpreter */
1464 PBlock(G);
1465 PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->unlock, "iO", 0, G->P_inst->cmd)); /* NOTE this may flush the command buffer! */
1466 PLockStatus(G);
1467 PyMOL_PopValidContext(G->PyMOL);
1468 PUnlockStatus(G);
1469 PUnlockGLUT(G);
1470 PUnblock(G);
1471 }
1472
1473 /**
1474 * - Release API lock without flushing
1475 * - Pop valid context
1476 * - Release GLUT lock
1477 * @pre no GIL
1478 */
PUnlockAPIAsGlutNoFlush(PyMOLGlobals * G)1479 void PUnlockAPIAsGlutNoFlush(PyMOLGlobals * G)
1480 { /* must call with unblocked interpreter */
1481 PBlock(G);
1482 PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->unlock, "iO", -1, G->P_inst->cmd)); /* prevents flushing of the buffer */
1483 PLockStatus(G);
1484 PyMOL_PopValidContext(G->PyMOL);
1485 PUnlockStatus(G);
1486 PUnlockGLUT(G);
1487 PUnblock(G);
1488 }
1489
1490 /**
1491 * Acquire API lock
1492 * @param block_if_busy If false and PyMOL is busy, do not block
1493 * @return False if API lock could not be acquired
1494 * @pre GIL
1495 */
get_api_lock(PyMOLGlobals * G,int block_if_busy)1496 static int get_api_lock(PyMOLGlobals * G, int block_if_busy)
1497 {
1498 if (!block_if_busy) {
1499 unique_PyObject_ptr got_lock(
1500 PYOBJECT_CALLFUNCTION(G->P_inst->lock_attempt, "O", G->P_inst->cmd));
1501
1502 ASSERT_PYOBJECT_NOT_NULL(got_lock, false);
1503
1504 if (PyObject_IsTrue(got_lock.get())) {
1505 return true;
1506 }
1507
1508 PLockStatus(G);
1509 bool busy = PyMOL_GetBusy(G->PyMOL, false);
1510 PUnlockStatus(G);
1511
1512 if (busy) {
1513 return false;
1514 }
1515 }
1516
1517 PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->lock, "O", G->P_inst->cmd));
1518 return true;
1519 }
1520
1521 /**
1522 * - Acquire GLUT lock
1523 * - Push valid context
1524 * - Acquire API lock
1525 * - Wait for glut_thread_keep_out == 0
1526 *
1527 * @param block_if_busy If false and PyMOL is busy, API lock is non-blocking
1528 * @return False if locks could not be acquired
1529 * @pre no GIL
1530 */
PLockAPIAsGlut(PyMOLGlobals * G,int block_if_busy)1531 int PLockAPIAsGlut(PyMOLGlobals * G, int block_if_busy)
1532 {
1533 PBlock(G);
1534
1535 PLockGLUT(G);
1536
1537 PLockStatus(G);
1538 PyMOL_PushValidContext(G->PyMOL);
1539 PUnlockStatus(G);
1540
1541 while (true) {
1542 if(!get_api_lock(G, block_if_busy)) {
1543 PLockStatus(G);
1544 PyMOL_PopValidContext(G->PyMOL);
1545 PUnlockStatus(G);
1546 PUnlockGLUT(G);
1547 PUnblock(G);
1548 return false; /* busy -- so allow main to update busy status display (if any) */
1549 }
1550
1551 if (G->P_inst->glut_thread_keep_out == 0) {
1552 break;
1553 }
1554
1555 /* IMPORTANT: keeps the glut thread out of an API operation... */
1556 /* NOTE: the keep_out variable can only be changed or read by the thread
1557 holding the API lock, therefore it is safe even through increment
1558 isn't atomic. */
1559
1560 PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->unlock, "iO", -1, G->P_inst->cmd)); /* prevent buffer flushing */
1561 #ifndef WIN32
1562 {
1563 struct timeval tv;
1564
1565 PUnblock(G);
1566 tv.tv_sec = 0;
1567 tv.tv_usec = 50000;
1568 select(0, NULL, NULL, NULL, &tv);
1569 PBlock(G);
1570 }
1571 #else
1572 /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
1573 PXDecRef(PYOBJECT_CALLFUNCTION(P_sleep, "f", 0.050));
1574 /* END PROPRIETARY CODE SEGMENT */
1575 #endif
1576 }
1577
1578 PUnblock(G); /* API is now locked, so we can free up Python... */
1579
1580 return true;
1581 }
1582
1583
1584 /* THESE CALLS ARE REQUIRED FOR MONOLITHIC COMPILATION TO SUCCEED UNDER WINDOWS. */
1585 #ifndef _PYMOL_EMBEDDED
1586
1587 #ifdef __cplusplus
1588 extern "C" {
1589 #endif
1590
1591 /*
1592 * void initExtensionClass(void);
1593 * void initsglite(void);
1594 */
1595 void init_champ(void);
1596
1597 #ifdef __cplusplus
1598 }
1599 #endif
1600 #endif
1601
1602 #ifdef _PYMOL_MONOLITHIC
1603 #ifndef _PYMOL_EMBEDDED
1604
1605 /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
1606 #ifdef WIN32
1607 #ifdef _PYMOL_NUMPY_INIT
1608 void init_numpy();
1609 void initmultiarray();
1610 void initarrayfns();
1611 void initlapack_lite();
1612 void initumath();
1613 void initranlib();
1614 #endif
1615 #endif
1616
1617 /* END PROPRIETARY CODE SEGMENT */
1618 #endif
1619 #endif
1620
1621 #ifdef _PYMOL_MONOLITHIC
1622 #ifndef _PYMOL_EMBEDDED
1623 #ifdef __cplusplus
1624 extern "C" {
1625 #endif
1626
1627 /*
1628 * void initExtensionClass(void);
1629 * void initsglite(void);
1630 */
1631 void init_champ(void);
1632 #ifdef _PYMOL_PYOMM
1633 void init_pyomm(void);
1634 #endif
1635
1636 #ifdef __cplusplus
1637 }
1638 #endif
1639 #endif
1640 #endif
1641
1642
1643 /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
1644 #if 0 // def WIN32
1645 static int IsSecurityRequired()
1646 {
1647 DWORD WindowsVersion = GetVersion();
1648 DWORD WindowsMajorVersion = (DWORD) (LOBYTE(LOWORD(WindowsVersion)));
1649 DWORD WindowsMinorVersion = (DWORD) (HIBYTE(LOWORD(WindowsVersion)));
1650
1651 if(WindowsVersion >= 0x80000000)
1652 return FALSE;
1653
1654 return TRUE;
1655 }
1656 #endif
1657
1658 /* END PROPRIETARY CODE SEGMENT */
1659
PSetupEmbedded(PyMOLGlobals * G,int argc,char ** argv)1660 void PSetupEmbedded(PyMOLGlobals * G, int argc, char **argv)
1661 {
1662 /* This routine is called if we are running with an embedded Python interpreter */
1663 PyObject *args;
1664
1665 /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
1666 #if 0 // def WIN32
1667
1668 /* Windows PyMOL now ships with Python 2.5 for both
1669 32 and 64 bit */
1670
1671 #ifndef EMBEDDED_PYTHONHOME
1672 #define EMBEDDED_PYTHONHOME "\\py27"
1673 #endif
1674
1675 { /* Automatically hide the window if this process was started as a
1676 vanilla console application (by double-clicking).
1677 Conveniently, this doesn't hide the window when launched from a
1678 command window. */
1679 HWND hwndFound;
1680 if(hwndFound = FindWindowA(NULL, argv[0])) {
1681 ShowWindow(hwndFound, SW_HIDE);
1682 }
1683 }
1684
1685 { /* if PYMOL_PATH and/or PYTHONHOME isn't in the environment coming
1686 in, then the user may simply have clicked PyMOL.exe, in which
1687 case we need to consult the registry regarding the location of
1688 the install */
1689
1690 static char line1[8092];
1691 static char line2[8092];
1692
1693 { /* If no PYMOL_PATH specific, but we were launched with an
1694 * absolute path, then try using that path first. With embedded
1695 * builds, the .EXE should always be located at the root of
1696 * $PYMOL_PATH */
1697
1698 char *pymol_path = getenv("PYMOL_PATH");
1699 if((!pymol_path) && (argc > 0) && argv[0][0] && (argv[0][1] == ':')
1700 && (argv[0][2] == '\\')) {
1701
1702 char *p;
1703 strcpy(line1, "PYMOL_PATH=");
1704 strcat(line1, argv[0]);
1705 p = line1 + strlen(line1);
1706 while(*p != '\\') {
1707 *p = 0;
1708 p--;
1709 }
1710 *p = 0;
1711 putenv(line1);
1712 }
1713 }
1714
1715 {
1716 OrthoLineType path_buffer;
1717 HKEY phkResult;
1718 DWORD lpcbData;
1719 DWORD lpType = REG_SZ;
1720 int r1, r2;
1721 char *pymol_path;
1722 char *pythonhome;
1723 int pythonhome_set = false;
1724 int restart_flag = false;
1725
1726 pymol_path = getenv("PYMOL_PATH");
1727 pythonhome = getenv("PYTHONHOME");
1728 if((!pymol_path) || (!pythonhome)) {
1729 lpcbData = sizeof(OrthoLineType) - 1;
1730 r1 = RegOpenKeyExA(HKEY_CLASSES_ROOT,
1731 #ifdef PYMOL_EVAL
1732 "Software\\PyMOL\\PyMOL Eval\\PYMOL_PATH",
1733 #else
1734 "Software\\PyMOL\\PyMOL\\PYMOL_PATH",
1735 #endif
1736 0, KEY_EXECUTE, &phkResult);
1737 if(r1 != ERROR_SUCCESS) {
1738 r1 = RegOpenKeyExA(HKEY_CURRENT_USER,
1739 #ifdef PYMOL_EVAL
1740 "Software\\PyMOL\\PyMOL Eval\\PYMOL_PATH",
1741 #else
1742 "Software\\PyMOL\\PyMOL\\PYMOL_PATH",
1743 #endif
1744 0, KEY_EXECUTE, &phkResult);
1745 }
1746 if(r1 == ERROR_SUCCESS) {
1747 r2 = RegQueryValueExA(phkResult, "", NULL, &lpType, (LPBYTE) path_buffer, &lpcbData);
1748 if(r2 == ERROR_SUCCESS) {
1749 /* use environment variable PYMOL_PATH first, registry entry
1750 second */
1751 if(!pymol_path) {
1752 strcpy(line1, "PYMOL_PATH=");
1753 strcat(line1, path_buffer);
1754 _putenv(line1);
1755 if(!pythonhome) { /* only set PYTHONHOME if already
1756 setting new PYMOL_PATH */
1757 pythonhome_set = true;
1758 strcpy(line2, "PYTHONHOME=");
1759 strcat(line2, path_buffer);
1760 strcat(line2, EMBEDDED_PYTHONHOME);
1761 restart_flag = true;
1762 _putenv(line2);
1763 }
1764 }
1765 }
1766 RegCloseKey(phkResult);
1767 }
1768 /* this allows us to just specify PYMOL_PATH with no registry entries */
1769 if((!pythonhome_set) && (pymol_path) && (!pythonhome)) {
1770 strcpy(line2, "PYTHONHOME=");
1771 strcat(line2, pymol_path);
1772 strcat(line2, EMBEDDED_PYTHONHOME);
1773 _putenv(line2);
1774 restart_flag = true;
1775 }
1776 }
1777 if(restart_flag && getenv("PYMOL_PATH") && getenv("PYTHONHOME")) {
1778
1779 /* now that we have the environment defined, restart the process
1780 * so that Python can use the new environment. If we don't do
1781 * this, then Python won't see the new environment vars. Why not? */
1782
1783 /* note that we use CreateProcesss to launch the console
1784 * application instead of exec or spawn in order to hide the
1785 * console window. Otherwise a console window might appear, and
1786 * that would suck. */
1787
1788 char command[8092];
1789 static char cmd_line[8092];
1790 char *p, *q;
1791 int a;
1792
1793 /* copy arguments, installing quotes around them */
1794
1795 sprintf(command, "%s\\pymol.exe", getenv("PYMOL_PATH"));
1796 p = cmd_line;
1797
1798 sprintf(p, "\"%s\"", command);
1799 p += strlen(p);
1800 *(p++) = ' ';
1801 *p = 0;
1802
1803 for(a = 1; a <= argc; a++) {
1804 q = argv[a];
1805 if(q) {
1806 if(*q != '"') { /* add quotes if not present */
1807 *(p++) = '"';
1808 while(*q) {
1809 *(p++) = *(q++);
1810 }
1811 *(p++) = '"';
1812 } else {
1813 while(*q) {
1814 *(p++) = *(q++);
1815 }
1816 }
1817 *(p++) = 32;
1818 *p = 0;
1819 }
1820 }
1821
1822 {
1823 LPSECURITY_ATTRIBUTES lpSA = NULL;
1824 PSECURITY_DESCRIPTOR lpSD = NULL;
1825 STARTUPINFOA si;
1826 PROCESS_INFORMATION pi;
1827 HANDLE hProcess = GetCurrentProcess();
1828
1829 ZeroMemory(&si, sizeof(STARTUPINFOA));
1830 si.cb = sizeof(STARTUPINFOA);
1831 si.dwFlags = STARTF_USESHOWWINDOW;
1832 si.wShowWindow = SW_HIDE;
1833
1834 if(IsSecurityRequired()) {
1835 lpSD = GlobalAlloc(GPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
1836 InitializeSecurityDescriptor(lpSD, SECURITY_DESCRIPTOR_REVISION);
1837 SetSecurityDescriptorDacl(lpSD, -1, 0, 0);
1838
1839 lpSA = (LPSECURITY_ATTRIBUTES) GlobalAlloc(GPTR, sizeof(SECURITY_ATTRIBUTES));
1840 lpSA->nLength = sizeof(SECURITY_ATTRIBUTES);
1841 lpSA->lpSecurityDescriptor = lpSD;
1842 lpSA->bInheritHandle = TRUE;
1843 }
1844
1845 if(CreateProcessA(NULL, (LPSTR) cmd_line, lpSA, NULL, TRUE,
1846 0, NULL, NULL, &si, &pi)) {
1847
1848 WaitForSingleObject(pi.hProcess, INFINITE);
1849 } else {
1850 printf("ERROR: Unable to restart PyMOL process with new environment:\n");
1851 system("set"); /* dump the environment. */
1852 printf("CreateProcess failed, code %d: %s\n", GetLastError(), cmd_line);
1853 printf("PyMOL will now terminate.\n");
1854 }
1855
1856 if(lpSA != NULL)
1857 GlobalFree(lpSA);
1858 if(lpSD != NULL)
1859 GlobalFree(lpSD);
1860 _exit(0);
1861 }
1862 }
1863 }
1864 }
1865 #endif
1866 /* END PROPRIETARY CODE SEGMENT */
1867
1868 /* compatibility for old compile-time defines */
1869
1870 #ifdef _PYMOL_SETUP_PY21
1871 #ifndef _PYMOL_SETUP_PY_EXT
1872 #define _PYMOL_SETUP_PY_EXT
1873 #endif
1874 #endif
1875 #ifdef _PYMOL_SETUP_PY22
1876 #ifndef _PYMOL_SETUP_PY_EXT
1877 #define _PYMOL_SETUP_PY_EXT
1878 #endif
1879 #endif
1880 #ifdef _PYMOL_SETUP_PY23
1881 #ifndef _PYMOL_SETUP_PY_EXT
1882 #define _PYMOL_SETUP_PY_EXT
1883 #endif
1884 #endif
1885 #ifdef _PYMOL_SETUP_PY24
1886 #ifndef _PYMOL_SETUP_PY_EXT
1887 #define _PYMOL_SETUP_PY_EXT
1888 #endif
1889 #endif
1890 #ifdef _PYMOL_SETUP_PY25
1891 #ifndef _PYMOL_SETUP_PY_EXT
1892 #define _PYMOL_SETUP_PY_EXT
1893 #endif
1894 #endif
1895 #ifdef _PYMOL_SETUP_PY26
1896 #ifndef _PYMOL_SETUP_PY_EXT
1897 #define _PYMOL_SETUP_PY_EXT
1898 #endif
1899 #endif
1900
1901 /* should we set up PYTHONHOME in the ext directory? */
1902
1903 #ifdef _PYMOL_SETUP_PY_EXT
1904 {
1905 static char line1[8092];
1906 static char line2[8092];
1907 if(!getenv("PYMOL_PATH")) { /* if PYMOL_PATH isn't defined... */
1908
1909 /* was our startup path absolute? */
1910
1911 if((argc > 0) && (argv[0][0] == '/')) {
1912 /* PYMOL was started with an absolute path, so try using that... */
1913 char *p;
1914 strcpy(line1, "PYMOL_PATH=");
1915 strcat(line1, argv[0]);
1916 p = line1 + strlen(line1);
1917 while(*p != '/') {
1918 *p = 0;
1919 p--;
1920 }
1921 *p = 0;
1922 putenv(line1);
1923 } else if((argc > 0) && getenv("PWD")
1924 && ((argv[0][0] == '.') || (strstr(argv[0], "/")))) {
1925 /* was the path relative? */
1926 char *p;
1927 strcpy(line1, "PYMOL_PATH=");
1928 strcat(line1, getenv("PWD"));
1929 strcat(line1, "/");
1930 strcat(line1, argv[0]);
1931 p = line1 + strlen(line1);
1932 while(*p != '/') {
1933 *p = 0;
1934 p--;
1935 }
1936 *p = 0;
1937 putenv(line1);
1938 } else { /* otherwise, just try using the current working directory */
1939 if(getenv("PWD")) {
1940 strcpy(line1, "PYMOL_PATH=");
1941 strcat(line1, getenv("PWD"));
1942 putenv(line1);
1943 }
1944 }
1945 }
1946
1947 /* now set PYTHONHOME so that we use the right binary libraries for
1948 this executable */
1949
1950 if(getenv("PYMOL_PATH")) {
1951 strcpy(line2, "PYTHONHOME=");
1952 strcat(line2, getenv("PYMOL_PATH"));
1953 strcat(line2, "/ext");
1954 putenv(line2);
1955 }
1956 }
1957 #endif
1958
1959 #ifndef _PYMOL_EMBEDDED
1960 Py_Initialize();
1961 PyEval_InitThreads();
1962 #endif
1963
1964 init_cmd();
1965
1966 #ifdef _PYMOL_MONOLITHIC
1967 #ifndef _PYMOL_EMBEDDED
1968 /*
1969 * initExtensionClass();
1970 * initsglite();
1971 */
1972 /* initialize champ */
1973 init_champ();
1974
1975 #ifdef _PYMOL_PYOMM
1976 init_pyomm();
1977 #endif
1978
1979 /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
1980 #ifdef WIN32
1981 #ifdef _PYMOL_NUMPY_INIT
1982 /* initialize numeric python */
1983 init_numpy();
1984 initmultiarray();
1985 initarrayfns();
1986 initlapack_lite();
1987 initumath();
1988 initranlib();
1989 #endif
1990 #endif
1991
1992 /* END PROPRIETARY CODE SEGMENT */
1993 #endif
1994 #endif
1995
1996 PyRun_SimpleString("import os\n");
1997 PyRun_SimpleString("import sys\n");
1998 /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
1999 #ifdef WIN32
2000 {
2001 /* getenv('PYMOL_PATH') and os.environ['PYMOL_PATH'] aren't
2002 automatically synchronized on Windows, so here we do the job
2003 manually... */
2004
2005 char *pymol_path = getenv("PYMOL_PATH");
2006 if(pymol_path) {
2007 PyObject *os = PyImport_AddModule("os"); /* borrowed ref */
2008 char *buffer = pymol::malloc<char>(strlen(pymol_path) + 100);
2009 if(os && buffer) {
2010 PyObject *envir = PyObject_GetAttrString(os, "environ");
2011 if(envir) {
2012 if(!PTruthCallStr1s(envir, "__contains__", "PYMOL_PATH")) {
2013 sprintf(buffer, "os.environ['PYMOL_PATH']=r'''%s'''\n", pymol_path);
2014 PyRun_SimpleString(buffer);
2015 }
2016 }
2017 PXDecRef(envir);
2018 }
2019 FreeP(buffer);
2020 }
2021 }
2022 /* ultimate fallback -- try using the current working directory */
2023 PyRun_SimpleString
2024 ("if 'PYMOL_PATH' not in os.environ: os.environ['PYMOL_PATH']=os.getcwd()\n");
2025 #endif
2026 /* END PROPRIETARY CODE SEGMENT */
2027
2028 P_main = PyImport_AddModule("__main__");
2029 if(!P_main)
2030 ErrFatal(G, "PyMOL", "can't find '__main__'");
2031
2032 args = PConvStringListToPyList(argc, argv); /* prepare our argument list */
2033 if(!args)
2034 ErrFatal(G, "PyMOL", "can't process arguments.");
2035
2036 /* copy arguments to __main__.pymol_argv */
2037 PyObject_SetAttrString(P_main, "pymol_argv", args);
2038 PyRun_SimpleString
2039 ("import __main__\nif not hasattr(sys,'argv'): sys.argv=__main__.pymol_argv");
2040
2041 PyRun_SimpleString("if (os.environ['PYMOL_PATH']+'/modules') not in sys.path: sys.path.insert(0,os.environ['PYMOL_PATH']+'/modules')\n"); /* needed for semistatic pymol */
2042 }
2043
PConvertOptions(CPyMOLOptions * rec,PyObject * options)2044 void PConvertOptions(CPyMOLOptions * rec, PyObject * options)
2045 {
2046 const char *load_str;
2047
2048 rec->pmgui = !PyInt_AsLong(PyObject_GetAttrString(options, "no_gui"));
2049 rec->internal_gui = PyInt_AsLong(PyObject_GetAttrString(options, "internal_gui"));
2050 rec->internal_feedback =
2051 PyInt_AsLong(PyObject_GetAttrString(options, "internal_feedback"));
2052 rec->show_splash = PyInt_AsLong(PyObject_GetAttrString(options, "show_splash"));
2053 rec->security = PyInt_AsLong(PyObject_GetAttrString(options, "security"));
2054 rec->game_mode = PyInt_AsLong(PyObject_GetAttrString(options, "game_mode"));
2055 rec->force_stereo = PyInt_AsLong(PyObject_GetAttrString(options, "force_stereo"));
2056 rec->winX = PyInt_AsLong(PyObject_GetAttrString(options, "win_x"));
2057 rec->winY = PyInt_AsLong(PyObject_GetAttrString(options, "win_y"));
2058 rec->winPX = PyInt_AsLong(PyObject_GetAttrString(options, "win_px"));
2059 rec->winPY = PyInt_AsLong(PyObject_GetAttrString(options, "win_py"));
2060 rec->blue_line = PyInt_AsLong(PyObject_GetAttrString(options, "blue_line"));
2061 rec->external_gui = PyInt_AsLong(PyObject_GetAttrString(options, "external_gui"));
2062 rec->siginthand = PyInt_AsLong(PyObject_GetAttrString(options, "sigint_handler"));
2063 rec->reuse_helper = PyInt_AsLong(PyObject_GetAttrString(options, "reuse_helper"));
2064 rec->auto_reinitialize =
2065 PyInt_AsLong(PyObject_GetAttrString(options, "auto_reinitialize"));
2066 rec->keep_thread_alive =
2067 PyInt_AsLong(PyObject_GetAttrString(options, "keep_thread_alive"));
2068 rec->quiet = PyInt_AsLong(PyObject_GetAttrString(options, "quiet"));
2069 #ifdef _PYMOL_IP_EXTRAS
2070 rec->incentive_product = true;
2071 PyObject_SetAttrString(options, "incentive_product", PyInt_FromLong(1));
2072 #else
2073 rec->incentive_product =
2074 PyInt_AsLong(PyObject_GetAttrString(options, "incentive_product"));
2075 #endif
2076 rec->multisample = PyInt_AsLong(PyObject_GetAttrString(options, "multisample"));
2077 rec->window_visible = PyInt_AsLong(PyObject_GetAttrString(options, "window_visible"));
2078 rec->read_stdin = PyInt_AsLong(PyObject_GetAttrString(options, "read_stdin"));
2079 rec->presentation = PyInt_AsLong(PyObject_GetAttrString(options, "presentation"));
2080 rec->defer_builds_mode =
2081 PyInt_AsLong(PyObject_GetAttrString(options, "defer_builds_mode"));
2082 rec->full_screen = PyInt_AsLong(PyObject_GetAttrString(options, "full_screen"));
2083 load_str = PyString_AsString(PyObject_GetAttrString(options, "after_load_script"));
2084 rec->sphere_mode = PyInt_AsLong(PyObject_GetAttrString(options, "sphere_mode"));
2085 rec->stereo_capable = PyInt_AsLong(PyObject_GetAttrString(options, "stereo_capable"));
2086 rec->stereo_mode = PyInt_AsLong(PyObject_GetAttrString(options, "stereo_mode"));
2087 rec->zoom_mode = PyInt_AsLong(PyObject_GetAttrString(options, "zoom_mode"));
2088 rec->no_quit = PyInt_AsLong(PyObject_GetAttrString(options, "no_quit"));
2089 rec->launch_status = PyInt_AsLong(PyObject_GetAttrString(options, "launch_status"));
2090 rec->gldebug = PyInt_AsLong(PyObject_GetAttrString(options, "gldebug"));
2091 rec->openvr_stub = PyInt_AsLong(PyObject_GetAttrString(options, "openvr_stub"));
2092
2093 if(load_str) {
2094 if(load_str[0]) {
2095 UtilNCopy(rec->after_load_script, load_str, PYMOL_MAX_OPT_STR);
2096 }
2097 }
2098 if(PyErr_Occurred()) {
2099 PyErr_Print();
2100 }
2101 }
2102
PGetOptions(CPyMOLOptions * rec)2103 void PGetOptions(CPyMOLOptions * rec)
2104 {
2105 PyObject *pymol, *invocation, *options;
2106
2107 pymol = PImportModuleOrFatal("pymol");
2108 invocation = PGetAttrOrFatal(pymol, "invocation"); /* get a handle to the invocation module */
2109 options = PGetAttrOrFatal(invocation, "options");
2110
2111 PConvertOptions(rec, options);
2112 Py_XDECREF(invocation);
2113 Py_XDECREF(options);
2114 Py_XDECREF(pymol);
2115 }
2116
2117 /**
2118 * Runs a string in the namespace of the pymol global module
2119 * @pre GIL
2120 */
PRunStringModule(PyMOLGlobals * G,const char * str)2121 void PRunStringModule(PyMOLGlobals * G, const char *str)
2122 {
2123 PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->exec, "Os", P_pymol, str));
2124 }
2125
2126 /**
2127 * Runs a string in the namespace of the pymol instance
2128 * @pre GIL
2129 */
PRunStringInstance(PyMOLGlobals * G,const char * str)2130 void PRunStringInstance(PyMOLGlobals * G, const char *str)
2131 {
2132 PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->exec, "Os", G->P_inst->obj, str));
2133 }
2134
WrapperObjectReset(WrapperObject * wo)2135 void WrapperObjectReset(WrapperObject *wo){
2136 if (wo->settingWrapperObject) {
2137 reinterpret_cast<SettingPropertyWrapperObject *>(wo->settingWrapperObject)->wobj = NULL;
2138 Py_DECREF(wo->settingWrapperObject);
2139 }
2140 #ifdef _PYMOL_IP_PROPERTIES
2141 if (wo->propertyWrapperObject) {
2142 reinterpret_cast<SettingPropertyWrapperObject *>(wo->propertyWrapperObject)->wobj = NULL;
2143 Py_DECREF(wo->propertyWrapperObject);
2144 }
2145 #endif
2146 Py_XDECREF(wo->dict);
2147 Py_DECREF(wo);
2148 }
2149
PInit(PyMOLGlobals * G,int global_instance)2150 void PInit(PyMOLGlobals * G, int global_instance)
2151 {
2152 /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
2153 #ifdef WIN32
2154 #ifdef _PYMOL_MONOLITHIC
2155 #ifndef _PYMOL_EMBEDDED
2156 #define _PYMOL_INIT_MODULES
2157 #endif
2158 #endif
2159 #endif
2160 /* END PROPRIETARY CODE SEGMENT */
2161
2162 #ifdef _PYMOL_INIT_MODULES
2163 /* Win32 module build: includes pyopengl, numpy, and sglite */
2164 /* sglite
2165 * initExtensionClass();
2166 * initsglite();
2167 */
2168 init_champ();
2169 #ifdef _PYMOL_PYOMM
2170 init_pyomm();
2171 #endif
2172
2173 /* initialize numeric python */
2174 #ifdef _PYMOL_NUMPY_INIT
2175 init_numpy();
2176 initmultiarray();
2177 initarrayfns();
2178 initlapack_lite();
2179 initumath();
2180 initranlib();
2181 #endif
2182 /* initialize PyOpenGL */
2183 #endif
2184
2185 #if PY_MAJOR_VERSION < 3
2186 // Support implicit utf-8 encoding (important for labeling!)
2187 // str(u"...unicode...") -> b"...utf-8..."
2188 // PyString_AsString(unicodeobj) -> "...utf-8..."
2189 PyUnicode_SetDefaultEncoding("utf-8");
2190 #endif
2191
2192 if(true /* global_instance */) {
2193 PCatchInit(); /* setup standard-output catch routine */
2194 }
2195
2196 P_pymol = PImportModuleOrFatal("pymol");
2197 P_pymol_dict = PyModule_GetDict(P_pymol);
2198 Py_XINCREF(P_pymol_dict);
2199 if(!P_pymol_dict)
2200 ErrFatal(G, "PyMOL", "can't find globals for 'pymol'");
2201
2202 if(global_instance) { /* if global singleton PyMOL... */
2203 G->P_inst = pymol::calloc<CP_inst>(1);
2204 G->P_inst->obj = P_pymol;
2205 G->P_inst->dict = P_pymol_dict;
2206 {
2207 int a;
2208 SavedThreadRec *str = G->P_inst->savedThread;
2209 for(a = 0; a < MAX_SAVED_THREAD; a++) {
2210 (str++)->id = -1;
2211 }
2212 }
2213 }
2214
2215 {
2216 G->P_inst->exec = PGetAttrOrFatal(P_pymol, "exec_str");
2217
2218 if(global_instance) {
2219 PCatch_install(NULL, NULL);
2220 }
2221
2222 P_traceback = PImportModuleOrFatal("traceback");
2223 P_cmd = PImportModuleOrFatal("pymol.cmd");
2224
2225 if(global_instance) {
2226 /* implies global singleton pymol, so set up the global handle */
2227 PyObject_SetAttrString(P_cmd, "_COb",
2228 PyCObject_FromVoidPtr((void *) &SingletonPyMOLGlobals,
2229 NULL));
2230
2231 /* cmd module is itself the api for the global PyMOL instance */
2232 G->P_inst->cmd = P_cmd;
2233 }
2234
2235 /* right now, all locks are global -- eventually some of these may
2236 become instance-specific in order to improve concurrency */
2237
2238 G->P_inst->lock = PGetAttrOrFatal(G->P_inst->cmd, "lock");
2239 G->P_inst->lock_attempt = PGetAttrOrFatal(G->P_inst->cmd, "lock_attempt");
2240 G->P_inst->unlock = PGetAttrOrFatal(G->P_inst->cmd, "unlock");
2241 G->P_inst->lock_api_status = PGetAttrOrFatal(G->P_inst->cmd, "lock_api_status");
2242 G->P_inst->lock_api_glut = PGetAttrOrFatal(G->P_inst->cmd, "lock_api_glut");
2243
2244 /* 'do' command */
2245
2246 G->P_inst->cmd_do = PGetAttrOrFatal(G->P_inst->cmd, "do");
2247
2248 /* cache */
2249 G->P_inst->cache = PyObject_GetAttrString(G->P_inst->obj, "_cache");
2250
2251 /* invariant stuff */
2252
2253 P_menu = PImportModuleOrFatal("pymol.menu");
2254 P_setting = PImportModuleOrFatal("pymol.setting");
2255 P_povray = PImportModuleOrFatal("pymol.povray");
2256
2257 #ifdef _PYMOL_XRAY
2258 P_xray = PImportModuleOrFatal("pymol.xray");
2259 #endif
2260
2261 /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
2262 #ifdef WIN32
2263 P_time = PImportModuleOrFatal("time");
2264 P_sleep = PGetAttrOrFatal(P_time, "sleep");
2265 #endif
2266 /* END PROPRIETARY CODE SEGMENT */
2267
2268 P_parser = PImportModuleOrFatal("pymol.parser");
2269
2270 {
2271 PyObject *fn_closure = PyObject_GetAttrString(P_parser, "new_parse_closure");
2272 G->P_inst->parse = PYOBJECT_CALLFUNCTION(fn_closure, "O", G->P_inst->cmd);
2273 PXDecRef(fn_closure);
2274 if(!G->P_inst->parse)
2275 ErrFatal(G, "PyMOL", "can't create 'parse' function closure");
2276 }
2277
2278 {
2279 PyObject *fn_closure = PyObject_GetAttrString(P_parser, "new_complete_closure");
2280 G->P_inst->complete = PYOBJECT_CALLFUNCTION(fn_closure, "O", G->P_inst->cmd);
2281 PXDecRef(fn_closure);
2282 if(!G->P_inst->complete)
2283 ErrFatal(G, "PyMOL", "can't create 'complete' function closure");
2284 }
2285
2286 {
2287 PyObject *fn_closure = PGetAttrOrFatal(P_pymol, "_colortype");
2288 G->P_inst->colortype = PYOBJECT_CALLFUNCTION(fn_closure, "O", G->P_inst->cmd);
2289 PXDecRef(fn_closure);
2290 }
2291
2292 P_chempy = PImportModuleOrFatal("chempy");
2293 P_models = PImportModuleOrFatal("chempy.models");
2294 P_CmdException = PGetAttrOrFatal(P_pymol, "CmdException");
2295 P_QuietException = PGetAttrOrFatal(P_cmd, "QuietException");
2296
2297 /* backwards compatibility */
2298
2299 PRunStringModule(G, "glutThread = thread.get_ident()");
2300
2301 P_glut_thread_id = PyThread_get_thread_ident();
2302
2303 #ifndef WIN32
2304 if(G->Option->siginthand) {
2305 signal(SIGINT, my_interrupt);
2306 }
2307 #endif
2308
2309 if (!Wrapper_Type.tp_basicsize) {
2310 Wrapper_Type.tp_basicsize = sizeof(WrapperObject);
2311 Wrapper_Type.tp_flags = Py_TPFLAGS_DEFAULT;
2312 wrapperMappingMethods.mp_length = NULL;
2313 wrapperMappingMethods.mp_subscript = &WrapperObjectSubScript;
2314 wrapperMappingMethods.mp_ass_subscript = &WrapperObjectAssignSubScript;
2315 Wrapper_Type.tp_as_mapping = &wrapperMappingMethods;
2316
2317 settingWrapper_Type.tp_basicsize = sizeof(SettingPropertyWrapperObject);
2318 settingWrapper_Type.tp_flags = Py_TPFLAGS_DEFAULT;
2319 settingWrapper_Type.tp_iter = &SettingWrapperObjectIter;
2320 settingMappingMethods.mp_length = NULL;
2321 settingMappingMethods.mp_subscript = &SettingWrapperObjectSubScript;
2322 settingMappingMethods.mp_ass_subscript = &SettingWrapperObjectAssignSubScript;
2323 settingWrapper_Type.tp_as_mapping = &settingMappingMethods;
2324 settingWrapper_Type.tp_getattro = PyObject_GenericGetAttrOrItem;
2325 settingWrapper_Type.tp_setattro = PyObject_GenericSetAttrAsItem;
2326
2327 if (PyType_Ready(&Wrapper_Type) < 0
2328 || PyType_Ready(&settingWrapper_Type) < 0
2329 ){
2330 PRINTFB(G, FB_Python, FB_Errors)
2331 " PInit: Wrapper_Type, settingWrapper_Type, propertyWrapper_Type not ready\n" ENDFB(G);
2332 return;
2333 }
2334 Py_INCREF(&Wrapper_Type);
2335 Py_INCREF(&settingWrapper_Type);
2336 }
2337 }
2338
2339 #ifdef _PYMOL_NO_MSGPACKC
2340 // fallback MMTF support
2341 PyRun_SimpleString(
2342 "import pymol.importing;"
2343 "pymol.importing.loadfunctions.setdefault('mmtf',"
2344 "pymol.importing.load_mmtf)");
2345 #endif
2346 }
2347
PPovrayRender(PyMOLGlobals * G,const char * header,const char * inp,const char * file,int width,int height,int antialias)2348 int PPovrayRender(PyMOLGlobals * G, const char *header, const char *inp, const char *file, int width,
2349 int height, int antialias)
2350 {
2351 PyObject *result;
2352 int ok;
2353 PBlock(G);
2354 result =
2355 PYOBJECT_CALLMETHOD(P_povray, "render_from_string", "sssiii", header, inp, file,
2356 width, height, antialias);
2357 ok = PyObject_IsTrue(result);
2358 Py_DECREF(result);
2359 PUnblock(G);
2360 return (ok);
2361 }
2362
PFree(PyMOLGlobals * G)2363 void PFree(PyMOLGlobals * G)
2364 {
2365 PXDecRef(G->P_inst->parse);
2366 PXDecRef(G->P_inst->complete);
2367 PXDecRef(G->P_inst->colortype);
2368 }
2369
2370 /**
2371 * Terminates the application with a call to `exit(code)`.
2372 */
PExit(PyMOLGlobals * G,int code)2373 void PExit(PyMOLGlobals * G, int code)
2374 {
2375 ExecutiveDelete(G, "all");
2376 PBlock(G);
2377
2378 PyMOL_PushValidContext(G->PyMOL);
2379 PyMOL_Stop(G->PyMOL);
2380 PyMOL_PopValidContext(G->PyMOL);
2381
2382 #ifndef _PYMOL_NO_MAIN
2383 if(G->Main) {
2384 MainFree();
2385 }
2386 #endif
2387
2388 PyMOL_Free(G->PyMOL);
2389
2390 #if 1
2391 /* we're having trouble with threading errors after calling Py_Exit,
2392 so for the time being, let's just take the process down at this
2393 point, instead of allowing PyExit to be called. */
2394
2395 exit(code);
2396 #else
2397
2398 Py_Exit(code);
2399 #endif
2400 }
2401
2402 /**
2403 * Add `str` to the command queue
2404 */
PParse(PyMOLGlobals * G,const char * str)2405 void PParse(PyMOLGlobals * G, const char *str)
2406 {
2407 OrthoCommandIn(G, str);
2408 }
2409
2410 /**
2411 * Call `cmd.do(str)`
2412 *
2413 * Effectively calls `PParse(str.splitlines())` with logging and echo.
2414 */
PDo(PyMOLGlobals * G,const char * str)2415 void PDo(PyMOLGlobals * G, const char *str)
2416 { /* assumes we already hold the re-entrant API lock */
2417 int blocked;
2418 PyObject *ret ;
2419 blocked = PAutoBlock(G);
2420 ret = PYOBJECT_CALLFUNCTION(G->P_inst->cmd_do, "s", str);
2421 Py_XDECREF(ret);
2422 PAutoUnblock(G, blocked);
2423 }
2424
2425 /*
2426 * Write `str` to the log file (if one is open).
2427 *
2428 * str: command or expression to log
2429 * format: cPLog_pml (`str` is PyMOL command)
2430 * cPLog_pym (`str` is Python expression)
2431 * cPLog_no_flush (write `str` as is)
2432 * cPLog_pml_lf (unused TODO remove?)
2433 *
2434 * See also equivalent Python impelemtation: cmd.log()
2435 */
PLog(PyMOLGlobals * G,const char * str,int format)2436 void PLog(PyMOLGlobals * G, const char *str, int format)
2437 {
2438 int mode;
2439 int a = sizeof(OrthoLineType) - 15;
2440 int blocked;
2441 PyObject *log;
2442 OrthoLineType buffer = "";
2443 mode = SettingGetGlobal_i(G, cSetting_logging);
2444 if(mode) {
2445 blocked = PAutoBlock(G);
2446 log = PyDict_GetItemString(P_pymol_dict, P_log_file_str);
2447 if(log && (log != Py_None)) {
2448 if(format == cPLog_no_flush) {
2449 PYOBJECT_CALLMETHOD(log, "write", "s", str); /* maximize responsiveness (for real-time) */
2450 } else {
2451 switch (mode) {
2452 case cPLog_pml: /* .pml file */
2453 switch (format) {
2454 case cPLog_pml_lf:
2455 strcpy(buffer, str);
2456 break;
2457 case cPLog_pml:
2458 case cPLog_pym:
2459 strcpy(buffer, str);
2460 strcat(buffer, "\n");
2461 break;
2462 }
2463 break;
2464 case cPLog_pym: /* .pym file */
2465 if((str[0] == '_') && (str[1]) == ' ')
2466 str += 2;
2467 switch (format) {
2468 case cPLog_pml_lf:
2469 a = strlen(str);
2470 while(a && str[a - 1] < 32) a--; /* trim CR/LF etc. */
2471 case cPLog_pml:
2472 if (str[0] == '/') {
2473 strncat(buffer, str + 1, a - 1);
2474 strcat(buffer, "\n");
2475 } else {
2476 strcpy(buffer, "cmd.do('''");
2477 char * b = buffer + strlen(buffer);
2478 for (; a && *str; --a) {
2479 if (*str == '\\' || *str == '\'') {
2480 *(b++) = '\\';
2481 }
2482 *(b++) = *(str++);
2483 }
2484 strcpy(b, "''')\n");
2485 }
2486 break;
2487 case cPLog_pym:
2488 strcpy(buffer, str);
2489 strcat(buffer, "\n");
2490 break;
2491 }
2492 }
2493 PYOBJECT_CALLMETHOD(log, "write", "s", buffer);
2494 PYOBJECT_CALLMETHOD(log, "flush", "");
2495 }
2496 }
2497 PAutoUnblock(G, blocked);
2498 }
2499 }
2500
2501 /**
2502 * Call flush() on the open log file handle
2503 */
PLogFlush(PyMOLGlobals * G)2504 void PLogFlush(PyMOLGlobals * G)
2505 {
2506 int mode;
2507 PyObject *log;
2508 int blocked;
2509 mode = SettingGetGlobal_i(G, cSetting_logging);
2510 if(mode) {
2511 blocked = PAutoBlock(G);
2512 log = PyDict_GetItemString(P_pymol_dict, P_log_file_str);
2513 if(log && (log != Py_None)) {
2514 PYOBJECT_CALLMETHOD(log, "flush", "");
2515 }
2516 PAutoUnblock(G, blocked);
2517 }
2518 }
2519
2520 #define PFLUSH_REPORT_UNCAUGHT_EXCEPTIONS(G) \
2521 if (PyErr_Occurred()) { \
2522 PyErr_Print(); \
2523 PRINTFB(G, FB_Python, FB_Errors) \
2524 " %s: Uncaught exception. PyMOL may have a bug.\n", __func__ ENDFB(G); \
2525 }
2526
2527 /**
2528 * Execute commands from the command queue.
2529 * If the current thread holds the GIL, do nothing. Otherwise the queue will
2530 * be empty after this call.
2531 * @return False if the queue was empty
2532 */
PFlush(PyMOLGlobals * G)2533 int PFlush(PyMOLGlobals * G)
2534 {
2535 /* NOTE: ASSUMES unblocked Python threads and a locked API */
2536 if(!OrthoCommandWaiting(G))
2537 return false;
2538
2539 if (PAutoBlock(G)) {
2540 if(!(PIsGlutThread() && G->P_inst->glut_thread_keep_out)) {
2541 /* don't run if we're currently banned */
2542 auto ortho = G->Ortho;
2543 while(!OrthoCommandIsEmpty(*ortho)){
2544 auto buffer = OrthoCommandOut(*ortho);
2545 OrthoCommandSetBusy(G, true);
2546 OrthoCommandNest(G, 1);
2547
2548 // TODO if we release here, another thread might acquire the API lock
2549 // and block us when we try to lock again. (see PYMOL-3249)
2550 // Example: set_key F1, ray async=1
2551 // => hitting F1 will block GUI updates, even though ray
2552 // is running async
2553 // PUnlockAPIWhileBlocked(G);
2554
2555 PFLUSH_REPORT_UNCAUGHT_EXCEPTIONS(G);
2556 PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->parse, "si", buffer.c_str(), 0));
2557 PFLUSH_REPORT_UNCAUGHT_EXCEPTIONS(G);
2558
2559 // TODO see above
2560 // PLockAPIWhileBlocked(G);
2561
2562 OrthoCommandSetBusy(G, false);
2563 /* make sure no commands left at this level */
2564 while(OrthoCommandWaiting(G))
2565 PFlushFast(G);
2566 OrthoCommandNest(G, -1);
2567 }
2568 }
2569 PUnblock(G);
2570 }
2571 return true;
2572 }
2573
2574 /**
2575 * Execute commands from the command queue.
2576 * @return False if the queue was empty
2577 * @pre GIL
2578 * @post queue is empty
2579 */
PFlushFast(PyMOLGlobals * G)2580 int PFlushFast(PyMOLGlobals * G)
2581 {
2582 /* NOTE: ASSUMES we currently have blocked Python threads and an unlocked API */
2583 int did_work = false;
2584 auto ortho = G->Ortho;
2585 while(!OrthoCommandIsEmpty(*ortho)){
2586 auto buffer = OrthoCommandOut(*ortho);
2587 OrthoCommandSetBusy(G, true);
2588 OrthoCommandNest(G, 1);
2589 did_work = true;
2590
2591 PFLUSH_REPORT_UNCAUGHT_EXCEPTIONS(G);
2592 PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->parse, "si", buffer.c_str(), 0));
2593 PFLUSH_REPORT_UNCAUGHT_EXCEPTIONS(G);
2594
2595 OrthoCommandSetBusy(G, false);
2596 /* make sure no commands left at this level */
2597 while(OrthoCommandWaiting(G))
2598 PFlushFast(G);
2599 OrthoCommandNest(G, -1);
2600 }
2601 return did_work;
2602 }
2603
PBlockLegacy()2604 void PBlockLegacy()
2605 {
2606 PBlock(SingletonPyMOLGlobals);
2607 }
2608
PUnblockLegacy()2609 void PUnblockLegacy()
2610 {
2611 PUnblock(SingletonPyMOLGlobals);
2612 }
2613
2614 /**
2615 * Acquire the GIL.
2616 * @pre no GIL
2617 * @post GIL
2618 */
PBlock(PyMOLGlobals * G)2619 void PBlock(PyMOLGlobals * G)
2620 {
2621
2622 if(!PAutoBlock(G)) {
2623 ErrFatal(G, "PBlock", "Threading error detected. Terminating...");
2624 }
2625 }
2626
2627 /**
2628 * Acquire the GIL.
2629 * Return false if the current thread already holds the GIL.
2630 */
PAutoBlock(PyMOLGlobals * G)2631 int PAutoBlock(PyMOLGlobals * G)
2632 {
2633 #ifndef _PYMOL_EMBEDDED
2634 SavedThreadRec *SavedThread = G->P_inst->savedThread;
2635
2636 auto id = PyThread_get_thread_ident();
2637
2638 for (auto a = MAX_SAVED_THREAD - 1; a; --a) {
2639 if (SavedThread[a].id == id) {
2640 // Aquire GIL
2641 PyEval_RestoreThread(SavedThread[a].state);
2642
2643 SavedThread[a].id = -1;
2644
2645 return 1;
2646 }
2647 }
2648 return 0;
2649 #else
2650 return 1;
2651 #endif
2652 }
2653
2654 /**
2655 * Can be called anytime, no lock conditions
2656 * @return True if the current thread is the GUI thread
2657 */
PIsGlutThread(void)2658 int PIsGlutThread(void)
2659 {
2660 return (PyThread_get_thread_ident() == P_glut_thread_id);
2661 }
2662
2663 /**
2664 * Release GIL
2665 * @pre GIL
2666 * @post no GIL
2667 */
PUnblock(PyMOLGlobals * G)2668 void PUnblock(PyMOLGlobals * G)
2669 {
2670 #ifndef _PYMOL_EMBEDDED
2671 SavedThreadRec *SavedThread = G->P_inst->savedThread;
2672 auto a = MAX_SAVED_THREAD - 1;
2673 /* NOTE: ASSUMES a locked API */
2674
2675 /* reserve a space while we have a lock */
2676 for (; a; --a) {
2677 if (SavedThread[a].id == -1) {
2678 SavedThread[a].id = PyThread_get_thread_ident();
2679 break;
2680 }
2681 }
2682
2683 // Release GIL
2684 SavedThread[a].state = PyEval_SaveThread();
2685 #endif
2686 }
2687
2688 /**
2689 * Release GIL if flag=true
2690 */
PAutoUnblock(PyMOLGlobals * G,int flag)2691 void PAutoUnblock(PyMOLGlobals * G, int flag)
2692 {
2693 if(flag)
2694 PUnblock(G);
2695 }
2696
2697 /**
2698 * Acquire GIL, release API lock and flush
2699 * @pre no GIL
2700 * @post GIL
2701 * @post no API lock
2702 */
PBlockAndUnlockAPI(PyMOLGlobals * G)2703 void PBlockAndUnlockAPI(PyMOLGlobals * G)
2704 {
2705 PBlock(G);
2706 PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->unlock, "iO", 0, G->P_inst->cmd));
2707 }
2708
2709 /**
2710 * Acquire API lock
2711 * @param block_if_busy If false and PyMOL is busy, do not block
2712 * @return False if API lock could not be acquired
2713 * @pre no GIL
2714 */
PLockAPI(PyMOLGlobals * G,int block_if_busy)2715 int PLockAPI(PyMOLGlobals * G, int block_if_busy)
2716 {
2717 int result = true;
2718 PBlock(G);
2719 if(block_if_busy) {
2720 PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->lock, "O", G->P_inst->cmd));
2721 } else { /* not blocking if PyMOL is busy */
2722
2723 PyObject *got_lock =
2724 PYOBJECT_CALLFUNCTION(G->P_inst->lock_attempt, "O", G->P_inst->cmd);
2725
2726 if(got_lock) {
2727 result = PyInt_AsLong(got_lock);
2728 Py_DECREF(got_lock);
2729 }
2730 }
2731 PUnblock(G);
2732 return result;
2733 }
2734
2735 /**
2736 * Release API lock with flushing
2737 * @pre no GIL
2738 * @pre API lock
2739 */
PUnlockAPI(PyMOLGlobals * G)2740 void PUnlockAPI(PyMOLGlobals * G)
2741 {
2742 PBlock(G);
2743 PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->unlock, "iO", 0, G->P_inst->cmd));
2744 PUnblock(G);
2745 }
2746
2747 /**
2748 * Release API lock without flushing
2749 * @pre GIL
2750 * @pre API lock
2751 * @post no API lock
2752 */
PUnlockAPIWhileBlocked(PyMOLGlobals * G)2753 static void PUnlockAPIWhileBlocked(PyMOLGlobals * G)
2754 {
2755 PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->unlock, "iO", -1, G->P_inst->cmd));
2756 }
2757
2758 /**
2759 * Acquire API lock
2760 * @pre GIL
2761 * @post GIL
2762 * @post API lock
2763 */
PLockAPIWhileBlocked(PyMOLGlobals * G)2764 static void PLockAPIWhileBlocked(PyMOLGlobals * G)
2765 {
2766 PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->lock, "O", G->P_inst->cmd));
2767 }
2768
2769 /**
2770 * Try to acquire API lock (non-blocking if busy) and release GIL
2771 * @return False if API lock could not be acquired
2772 * @pre GIL
2773 * @post no GIL if API lock could be acquired
2774 */
PTryLockAPIAndUnblock(PyMOLGlobals * G)2775 int PTryLockAPIAndUnblock(PyMOLGlobals * G)
2776 {
2777 int result = get_api_lock(G, false);
2778 if(result) {
2779 PUnblock(G);
2780 }
2781 return result;
2782 }
2783
2784 /**
2785 * Acquire API lock and release the GIL
2786 * @pre GIL
2787 * @post no GIL
2788 * @post API Lock
2789 */
PLockAPIAndUnblock(PyMOLGlobals * G)2790 void PLockAPIAndUnblock(PyMOLGlobals * G)
2791 {
2792 PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->lock, "O", G->P_inst->cmd));
2793 PUnblock(G);
2794 }
2795
2796 /**
2797 * Assign a variable in the pymol namespace, like
2798 * @verbatim
2799 import pymol
2800 setattr(pymol, name, value)
2801 @endverbatim
2802 * @pre no GIL
2803 */
PDefineFloat(PyMOLGlobals * G,const char * name,float value)2804 void PDefineFloat(PyMOLGlobals * G, const char *name, float value)
2805 {
2806 char buffer[OrthoLineLength];
2807 sprintf(buffer, "%s = %f\n", name, value);
2808 PBlock(G);
2809 PRunStringModule(G, buffer);
2810 PUnblock(G);
2811 }
2812
2813
2814
2815 /* A static module */
2816
PCatchWrite(PyObject * self,PyObject * args)2817 static PyObject *PCatchWrite(PyObject * self, PyObject * args)
2818 {
2819 char *str;
2820 PyArg_ParseTuple(args, "s", &str);
2821 if(str[0]) {
2822 if(SingletonPyMOLGlobals) {
2823 if(Feedback(SingletonPyMOLGlobals, FB_Python, FB_Output)) {
2824 OrthoAddOutput(SingletonPyMOLGlobals, str);
2825 }
2826 }
2827 }
2828 return PConvAutoNone(Py_None);
2829 }
2830
PCatchWritelines(PyObject * self,PyObject * args)2831 static PyObject *PCatchWritelines(PyObject * self, PyObject * args)
2832 {
2833 PyObject *seq;
2834 int len;
2835 PyArg_ParseTuple(args, "O", &seq);
2836 if(seq && PySequence_Check(seq)) {
2837 if((len = PySequence_Size(seq)) > 0) {
2838 int i;
2839 for(i = 0; i < len; i++) {
2840 PyObject *obj = PySequence_GetItem(seq, i);
2841 if(obj && PyString_Check(obj)) {
2842 const char *str = PyString_AsString(obj);
2843 if(SingletonPyMOLGlobals) {
2844 if(Feedback(SingletonPyMOLGlobals, FB_Python, FB_Output)) {
2845 OrthoAddOutput(SingletonPyMOLGlobals, str);
2846 }
2847 }
2848 }
2849 Py_XDECREF(obj);
2850 }
2851 }
2852 }
2853 return PConvAutoNone(Py_None);
2854 }
2855
PCatchFlush(PyObject * self,PyObject * args)2856 static PyObject *PCatchFlush(PyObject * self, PyObject * args)
2857 {
2858 fflush(stdout);
2859 fflush(stderr);
2860 return PConvAutoNone(Py_None);
2861 }
2862
PCatchIsAtty(PyObject * self,PyObject * args)2863 static PyObject *PCatchIsAtty(PyObject * self, PyObject * args)
2864 {
2865 Py_RETURN_FALSE;
2866 }
2867
PCatch_install(PyObject * self,PyObject * args)2868 static PyObject *PCatch_install(PyObject * self, PyObject * args)
2869 {
2870 PyRun_SimpleString(
2871 "import sys, pcatch\n"
2872 "if sys.stdout is not pcatch:"
2873 "pcatch.closed = False;"
2874 "pcatch.encoding = 'UTF-8';"
2875 "sys.stderr = sys.stdout = pcatch");
2876 return PConvAutoNone(Py_None);
2877 }
2878
2879 static PyMethodDef PCatch_methods[] = {
2880 {"writelines", PCatchWritelines, METH_VARARGS},
2881 {"write", PCatchWrite, METH_VARARGS},
2882 {"flush", PCatchFlush, METH_VARARGS},
2883 {"isatty", PCatchIsAtty, METH_VARARGS}, // called by pip.main(["install", "..."])
2884 {"_install", PCatch_install, METH_VARARGS},
2885 {NULL, NULL} /* sentinel */
2886 };
2887
PCatchInit(void)2888 void PCatchInit(void)
2889 {
2890 #if PY_MAJOR_VERSION < 3
2891 PyImport_AddModule("pcatch");
2892 Py_InitModule("pcatch", PCatch_methods);
2893 #else
2894 static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT,
2895 "pcatch", NULL, -1, PCatch_methods };
2896 PyObject * pcatch = PyModule_Create(&moduledef);
2897 if (pcatch) {
2898 PyDict_SetItemString(PyImport_GetModuleDict(), "pcatch", pcatch);
2899 Py_DECREF(pcatch);
2900 }
2901 #endif
2902 }
2903 #endif
2904