def _generate_index(source, logger): # Take contents of source, look for lists of tools and commands, # and insert tools and commands from bundles that come with # documentation from chimerax import app_dirs user_dir = os.path.join(app_dirs.user_cache_dir, 'docs', 'user') path = os.path.join(user_dir, 'index.html') if os.path.exists(path): return path os.makedirs(user_dir, exist_ok=True) from chimerax.core import toolshed ts = toolshed.get_toolshed() if ts is None: return None # Look for <div id="foobar"> import lxml.html html = lxml.html.parse(source) for node in html.iterfind(".//div[@id]"): ident = node.attrib["id"] if ident == "clist": _update_list(ts, node, 'commands', _update_commands, logger) elif ident == "tlist": _update_list(ts, node, 'tools', _update_tools, logger) data = lxml.html.tostring(html) os.makedirs(user_dir, exist_ok=True) with open(path, 'wb') as f: f.write(data) return path
def take_snapshot(self, session, flags): """For session saving""" from chimerax.core.toolshed import get_toolshed ts = get_toolshed() return { 'version': 2, 'seqs': self._seqs, 'ident': self.ident, 'file attrs': self.file_attrs, 'file markups': self.file_markups, 'associations': self.associations, 'match maps': [s.match_maps for s in self._seqs], 'auto_destroy': self.auto_destroy, 'auto_associate': self.auto_associate, 'description': self.description, 'intrinsic': self.intrinsic, 'sseq to chain': self._sseq_to_chain, 'headers': [(ts.find_bundle_for_class(hdr.__class__).name, hdr.__class__.__name__, hdr.get_state()) for hdr in self.headers] }
def initialize(session, bundle_info): global _new_bundle_handler ts = toolshed.get_toolshed() _new_bundle_handler = ts.triggers.add_handler( toolshed.TOOLSHED_BUNDLE_INSTALLED, _update_cache) # ? = ts.triggers.add_handler( # toolshed.TOOLSHED_BUNDLE_UNINSTALLED, _update_cache) _update_cache()
def delete(self): if self._handlers: from chimerax.core.toolshed import get_toolshed ts = get_toolshed() if ts: for h in self._handlers: ts.triggers.remove_handler(h) self._handlers = None super().delete()
def restore_snapshot(session, data): """For restoring scenes/sessions""" ident = data['ident'] if 'ident' in data else data['name'] create_headers = data['version'] < 2 aln = Alignment(session, data['seqs'], ident, data['file attrs'], data['file markups'], data['auto_destroy'], "session" if data['auto_associate'] else False, data.get('description', ident), data.get('intrinsic', False), create_headers=create_headers, session_restore=True) aln.associations = data['associations'] for s, mm in zip(aln.seqs, data['match maps']): s.match_maps = mm for chain, match_map in mm.items(): match_map.mod_handler = match_map.triggers.add_handler( 'modified', aln._mmap_mod_cb) if 'sseq to chain' in data: aln._sseq_to_chain = data['sseq to chain'] from chimerax.core.toolshed import get_toolshed ts = get_toolshed() if not create_headers: for bundle_name, class_name, header_state in data['headers']: bundle = ts.find_bundle(bundle_name, session.logger, installed=True) if not bundle: bundle = ts.find_bundle(bundle_name, session.logger, installed=False) if bundle: session.logger.error( "You need to install bundle %s in order to restore" " alignment header of type %s" % (bundle_name, class_name)) else: session.logger.error( "Cannot restore alignment header of type %s due to" " being unable to find any bundle named %s" % (class_name, bundle_name)) continue header_class = bundle.get_class(class_name, session.logger) if header_class: aln._headers.append( header_class.session_restore(session, aln, header_state)) else: session.logger.warning( "Could not find alignment header class %s" % class_name) aln._session_restore = False return aln
def _setup(self): # # Register for updates of name register/deregister events # session = self.session from chimerax.core.toolshed import get_toolshed ts = get_toolshed() t1 = ts.triggers.add_handler("selector registered", self.update_names) t2 = ts.triggers.add_handler("selector deregistered", self.update_names) self._handlers = (t1, t2)
def _get_bundle_dirs(self, logger, dep): from chimerax.core import toolshed from pkg_resources import Requirement, get_distribution req = Requirement.parse(dep) if not get_distribution(req): raise RuntimeError("unsatisfied dependency: %s" % dep) ts = toolshed.get_toolshed() bundle = ts.find_bundle(req.project_name, logger) if not bundle: # The requirement is satisfied but is not recognized # as a bundle. Probably just a regular Python package. return None, None inc = bundle.include_dir() lib = bundle.library_dir() return inc, lib
def make_install(self, session, debug=False, user=None, no_deps=None): self.make_wheel(debug=debug) from chimerax.core.commands import run, FileNameArg cmd = "toolshed install %s" % FileNameArg.unparse(self.wheel_path) if user is not None: if user: cmd += " user true" else: cmd += " user false" if no_deps is not None: if no_deps: cmd += " noDeps true" else: cmd += " noDeps false" from chimerax.core import toolshed ts = toolshed.get_toolshed() bundle = ts.find_bundle(self.name, session.logger) if bundle is not None: cmd += " reinstall true" run(session, cmd)
def finish(session, bundle_info): global _new_bundle_handler ts = toolshed.get_toolshed() if _new_bundle_handler is not None: ts.triggers.remove_handler(_new_bundle_handler) _new_bundle_handler = None
def init(argv, event_loop=True): import sys if sys.platform.startswith('darwin'): paths = os.environ['PATH'].split(':') if '/usr/sbin' not in paths: # numpy, numexpr, and pytables need sysctl in path paths.append('/usr/sbin') os.environ['PATH'] = ':'.join(paths) del paths if sys.platform.startswith('linux'): # Workaround for #638: # "any number of threads more than one leads to 200% CPU usage" os.environ["OPENBLAS_NUM_THREADS"] = "1" # Setup SSL CA certificates file # This used to be only necessary for darwin, but Windows # appears to need it as well. So use it for all platforms. import certifi os.environ["SSL_CERT_FILE"] = certifi.where() # distlib, since 0.2.8, does not recognize "Obsoletes" as a legal # metadata classifier, but jurko 0.6 (SOAP package) claims to be # Metadata-Version 2.1 but specifies Obsolete. Hack below makes # Obsolete not cause a traceback. from distlib import metadata try: if "Obsoletes" not in metadata._566_FIELDS: metadata._566_FIELDS = metadata._566_FIELDS + ("Obsoletes", ) except AttributeError: pass opts, args = parse_arguments(argv) # install line_profile decorator, and install it before # initialize_ssl_cert_dir() in case the line profiling is in the # core (which would cause initialize_ssl_cert_dir() to fail) import builtins if not opts.line_profile: builtins.__dict__['line_profile'] = lambda x: x else: # write profile results on exit import atexit import line_profiler prof = line_profiler.LineProfiler() builtins.__dict__['line_profile'] = prof atexit.register(prof.dump_stats, "%s.lprof" % app_name) from chimerax.core.utils import initialize_ssl_cert_dir initialize_ssl_cert_dir() # find chimerax.core's version # we cannot use pip for this because we want to update # site.USER_SITE before importing pip, and site.USER_SITE # depends on the version try: from chimerax.core import version except ImportError: print("error: unable to figure out %s's version" % app_name) return os.EX_SOFTWARE if opts.use_defaults: from chimerax.core import configinfo configinfo.only_use_defaults = True from chimerax import core if opts.offscreen: # Flag to configure off-screen rendering before PyOpenGL imported core.offscreen_rendering = True opts.gui = False if not opts.gui and opts.load_tools: # only load tools if we have a GUI opts.load_tools = False is_root = False # On Linux, don't create user directories if root (the installer uid) # figure out the user/system directories for application # invoked with -m ChimeraX_main, so argv[0] is full path to ChimeraX_main # Windows: # 'C:\\...\\ChimeraX.app\\bin\\lib\\site-packages\\ChimeraX_main.py' # Linux: # '/.../ChimeraX.app/lib/python3.5/site-packages/ChimeraX_main.py' # Mac OS X: # '/.../ChimeraX.app/Contents/lib/python3.5/site-packages/ChimeraX_main.py' # '/.../ChimeraX.app/Contents/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/ChimeraX_main.py' # TODO: more robust way dn = os.path.dirname rootdir = dn(dn(dn(dn(sys.argv[0])))) if sys.platform.startswith('darwin'): rootdir = dn(dn(dn(dn(dn(rootdir))))) if sys.platform.startswith('linux'): os.environ['XDG_CONFIG_DIRS'] = rootdir is_root = os.getuid() == 0 if is_root: # ensure toolshed cache is not written os.environ['HOME'] = "/non/existent/directory" if sys.platform.startswith('win'): if 'HOME' in os.environ: # Windows uses HOMEPATH and HOMEDRIVE, so HOME's presence indicates # a non-standard startup environment. So remove HOME to make # sure the the correct application paths are figured out. del os.environ['HOME'] import ctypes # getpn = ctypes.pythonapi.Py_GetProgramName # getpn.restype = ctypes.c_wchar_p # pn = getpn() # assert(os.path.dirname(pn) == rootdir) # Python uses LoadLibraryEx with LOAD_WITH_ALTERED_SEARCH_PATH to # search in directory of library first instead of the directory of # application binary. So add back the "bin" directory, which is # the Windows equivalent of the Linux/Mac OS X rpath directory. setdlldir = ctypes.windll.kernel32.SetDllDirectoryW setdlldir.argtypes = [ctypes.c_wchar_p] setdlldir.restype = ctypes.c_bool setdlldir(os.path.join(rootdir, 'bin')) from distlib.version import NormalizedVersion as Version epoch, ver, *_ = Version(version).parse(version) if len(ver) == 1: ver += (0, ) partial_version = '%s.%s' % (ver[0], ver[1]) import chimerax import appdirs chimerax.app_dirs = ad = appdirs.AppDirs(app_name, appauthor=app_author, version=partial_version) if not is_root: # make sure app_dirs.user_* directories exist for var, name in (('user_data_dir', "user's data"), ('user_config_dir', "user's configuration"), ('user_cache_dir', "user's cache")): directory = getattr(ad, var) try: os.makedirs(directory, exist_ok=True) except OSError as e: print("Unable to make %s directory: %s: %s" % (name, e.strerror, e.filename), file=sys.stderr) return os.EX_CANTCREAT # app_dirs_unversioned is primarily for caching data files that will # open in any version # app_dirs_unversioned.user_* directories are parents of those in app_dirs chimerax.app_dirs_unversioned = adu = appdirs.AppDirs(app_name, appauthor=app_author) # update "site" user variables to use ChimeraX instead of Python paths # NB: USER_SITE is used by both pip and the toolshed, so # this must happen before pip is imported so that "--user" installs # will go in the right place. import site if not is_root: site.USER_BASE = adu.user_data_dir site.USER_SITE = os.path.join(ad.user_data_dir, "site-packages") else: from distutils import sysconfig site.USER_SITE = sysconfig.get_python_lib() # Find the location of "share" directory so that we can inform # the C++ layer. Assume it's a sibling of the directory that # the executable is in. chimerax.app_bin_dir = os.path.join(rootdir, "bin") if sys.platform.startswith('win'): chimerax.app_data_dir = os.path.join(chimerax.app_bin_dir, "share") else: chimerax.app_data_dir = os.path.join(rootdir, "share") chimerax.app_lib_dir = os.path.join(rootdir, "lib") # inform the C++ layer of the appdirs paths from chimerax.core import _appdirs _appdirs.init_paths(os.sep, ad.user_data_dir, ad.user_config_dir, ad.user_cache_dir, ad.site_data_dir, ad.site_config_dir, ad.user_log_dir, chimerax.app_data_dir, adu.user_cache_dir) from chimerax.core import session try: sess = session.Session(app_name, debug=opts.debug, silent=opts.silent, minimal=opts.safe_mode) except ImportError as err: if opts.offscreen and 'OpenGL' in err.args[0]: if sys.platform.startswith("linux"): why = "failed" else: why = "not supported on this platform" print("Offscreen rendering is", why, file=sys.stderr) return os.EX_UNAVAILABLE raise from chimerax.core import core_settings core_settings.init(sess) session.common_startup(sess) if opts.uninstall: return uninstall(sess) # initialize qt if opts.gui: from chimerax.ui import initialize_qt initialize_qt() # initialize the user interface if opts.gui: from chimerax.ui import gui ui_class = gui.UI else: from chimerax.core import nogui ui_class = nogui.UI if opts.color is not None: nogui._color_output = opts.color if opts.color: def t(): return True sys.stdout.isatty = t sys.stderr.isatty = t # sets up logging, splash screen if gui # calls "sess.save_in_session(self)" sess.ui = ui_class(sess) sess.ui.stereo = opts.stereo sess.ui.autostart_tools = opts.load_tools # Set current working directory to Desktop when launched from icon. if ((sys.platform.startswith('darwin') and os.getcwd() == '/') or (sys.platform.startswith('win') and os.getcwd().endswith('\\Users\\Public\\Desktop'))): try: os.chdir(os.path.expanduser('~/Desktop')) except Exception: pass # splash screen if opts.gui: sess.ui.show_splash() num_splash_steps = 2 if opts.gui: num_splash_steps += 1 if not opts.gui and opts.load_tools: num_splash_steps += 1 import itertools splash_step = itertools.count() # common core initialization if not opts.silent: sess.ui.splash_info("Initializing core", next(splash_step), num_splash_steps) if sess.ui.is_gui and opts.debug: print("Initializing core", flush=True) # Install any bundles before toolshed is initialized so # the new ones get picked up in this session rebuild_cache = False from chimerax.core import toolshed inst_dir, restart_file = toolshed.restart_action_info() restart_action_msgs = [] if os.path.exists(restart_file): # Move file out of the way so next restart of ChimeraX # (when we try to install the bundle) will not go into # an infinite loop reopening the restart file tmp_file = restart_file + ".tmp" try: # Remove in case old file lying around. # Windows does not allow renaming to an existing file. os.remove(tmp_file) except Exception: pass os.rename(restart_file, tmp_file) with open(tmp_file) as f: for line in f: sess.ui.splash_info("Restart action:\n%s" % line) restart_action(line, inst_dir, restart_action_msgs) os.remove(tmp_file) rebuild_cache = True if opts.toolshed is None: # Default to whatever the restart actions needed toolshed_url = _restart_toolshed_url elif opts.toolshed == "preview": toolshed_url = toolshed.preview_toolshed_url() else: toolshed_url = opts.toolshed toolshed.init(sess.logger, debug=sess.debug, rebuild_cache=rebuild_cache, check_available=opts.get_available_bundles, remote_url=toolshed_url, ui=sess.ui) sess.toolshed = toolshed.get_toolshed() if opts.module != 'pip': # keep bugs in ChimeraX from preventing pip from working if not opts.silent: sess.ui.splash_info("Initializing bundles", next(splash_step), num_splash_steps) if sess.ui.is_gui and opts.debug: print("Initializing bundles", flush=True) sess.toolshed.bootstrap_bundles(sess, opts.safe_mode) from chimerax.core import tools sess.tools = tools.Tools(sess, first=True) from chimerax.core import tasks sess.tasks = tasks.Tasks(sess, first=True) from chimerax.core import undo sess.undo = undo.Undo(sess, first=True) if opts.version >= 0: sess.silent = False if opts.version > 3: opts.version = 3 format = [None, 'verbose', 'bundles', 'packages'][opts.version] from chimerax.core.commands import command_function version_cmd = command_function("version") version_cmd(sess, format) return os.EX_OK if opts.list_io_formats: sess.silent = False collate = {} for fmt in sess.data_formats.formats: collate.setdefault(fmt.category, []).append(fmt) categories = list(collate.keys()) categories.sort(key=str.casefold) print("Supported file suffixes:") print(" o = open, s = save") openers = set(sess.open_command.open_data_formats) savers = set(sess.save_command.save_data_formats) for cat in categories: print("\n%s:" % cat) fmts = collate[cat] fmts.sort(key=lambda fmt: fmt.name.casefold()) for fmt in fmts: o = 'o' if fmt in openers else ' ' s = 's' if fmt in savers else ' ' if fmt.suffixes: exts = ': ' + ', '.join(fmt.suffixes) else: exts = '' print("%c%c %s%s" % (o, s, fmt.name, exts)) # TODO: show database formats # TODO: show mime types? # TODO: show compression suffixes? return os.EX_OK if opts.gui: # build out the UI, populate menus, create graphics, etc. if not opts.silent: sess.ui.splash_info("Starting main interface", next(splash_step), num_splash_steps) if sess.ui.is_gui and opts.debug: print("Starting main interface", flush=True) sess.ui.build() if opts.start_tools: if not opts.silent: msg = 'Starting tools %s' % ', '.join(opts.start_tools) sess.ui.splash_info(msg, next(splash_step), num_splash_steps) if sess.ui.is_gui and opts.debug: print(msg, flush=True) # canonicalize tool names start_tools = [] for t in opts.start_tools: tools = sess.toolshed.find_bundle_for_tool(t) if not tools: sess.logger.warning("Unable to find tool %s" % repr(t)) continue start_tools.append(tools[0][1]) sess.tools.start_tools(start_tools) if opts.commands: if not opts.silent: msg = 'Running startup commands' # sess.ui.splash_info(msg, next(splash_step), num_splash_steps) if sess.ui.is_gui and opts.debug: print(msg, flush=True) from chimerax.core.commands import run for cmd in opts.commands: run(sess, cmd) if opts.scripts: if not opts.silent: msg = 'Running startup scripts' # sess.ui.splash_info(msg, next(splash_step), num_splash_steps) if sess.ui.is_gui and opts.debug: print(msg, flush=True) from chimerax.core.commands import run for script in opts.scripts: run(sess, 'runscript %s' % script) if not opts.silent: sess.ui.splash_info("Finished initialization", next(splash_step), num_splash_steps) if sess.ui.is_gui and opts.debug: print("Finished initialization", flush=True) if opts.gui: sess.ui.close_splash() if not opts.silent: from chimerax.core.logger import log_version log_version(sess.logger) # report version in log if opts.gui or hasattr(core, 'offscreen_rendering'): sess.update_loop.start_redraw_timer() sess.logger.info( '<a href="cxcmd:help help:credits.html">How to cite UCSF ChimeraX</a>', is_html=True) # Show any messages from installing bundles on restart if restart_action_msgs: for where, msg in restart_action_msgs: if where == "stdout": sess.logger.info(msg) else: sess.logger.warning(msg) if opts.module: import runpy import warnings sys.argv[:] = args # runpy will insert appropriate argv[0] exit = SystemExit(os.EX_OK) with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=BytesWarning) global_dict = {'session': sess} try: runpy.run_module(opts.module, init_globals=global_dict, run_name='__main__', alter_sys=True) except SystemExit as e: exit = e if opts.module == 'pip' and exit.code == os.EX_OK: has_install = 'install' in sys.argv has_uninstall = 'uninstall' in sys.argv if has_install or has_uninstall: per_user = '******' in sys.argv sess.toolshed.reload(sess.logger, rebuild_cache=True) sess.toolshed.set_install_timestamp(per_user) # Do not remove scripts anymore since we may be installing # using ChimeraX which would put the right paths in # generated files. # if has_install: # remove_python_scripts(chimerax.app_bin_dir) return exit.code from chimerax.core import startup startup.run_user_startup_scripts(sess) if opts.cmd: # Emulated Python's -c option. # This is needed for -m pip to work in some cases. sys.argv = args sys.argv[0] = '-c' global_dict = { 'session': sess, '__name__': '__main__', } exec(opts.cmd, global_dict) return os.EX_OK # the rest of the arguments are data files from chimerax.core import errors, commands for arg in args: if opts.safe_mode: # 'open' command unavailable; only open Python files if not arg.endswith('.py'): sess.logger.error( "Can only open Python scripts in safe mode, not '%s'" % arg) return os.EX_SOFTWARE from chimerax.core.scripting import open_python_script try: open_python_script(sess, open(arg, 'rb'), arg) except (IOError, errors.NotABug) as e: sess.logger.error(str(e)) return os.EX_SOFTWARE except Exception: import traceback traceback.print_exc() return os.EX_SOFTWARE else: from chimerax.core.commands import StringArg try: commands.run(sess, 'open %s' % StringArg.unparse(arg)) except (IOError, errors.NotABug) as e: sess.logger.error(str(e)) return os.EX_SOFTWARE except Exception: import traceback traceback.print_exc() return os.EX_SOFTWARE # Open files dropped on application if opts.gui: sess.ui.open_pending_files(ignore_files=args) # Allow the event_loop to be disabled, so we can be embedded in # another application if event_loop and opts.event_loop: try: sess.ui.event_loop() except SystemExit as e: return e.code except Exception: import traceback traceback.print_exc() return os.EX_SOFTWARE return os.EX_OK