diff --git a/.gitignore b/.gitignore index 8c7536034..fd94b9ba4 100755 --- a/.gitignore +++ b/.gitignore @@ -944,8 +944,8 @@ MEMO.md CLAUDE.md docs/sphinx/_build/ -# Pre-built docs (generated by sphinx-build) -src/*/_sphinx_html/ +# PS-128: do NOT ignore src//_sphinx_html/ — scitex-cloud serves +# in-wheel docs from that path, so the bundle must be tracked. .playwright-cli/ .venv/ # Claude worktree symlink diff --git a/src/scitex/_sphinx_html/.buildinfo b/src/scitex/_sphinx_html/.buildinfo new file mode 100644 index 000000000..bac671ae1 --- /dev/null +++ b/src/scitex/_sphinx_html/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file records the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 7dc761b96e0d37a6fb81bc449771db72 +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/src/scitex/_sphinx_html/.nojekyll b/src/scitex/_sphinx_html/.nojekyll new file mode 100644 index 000000000..e69de29bb diff --git a/src/scitex/_sphinx_html/_modules/index.html b/src/scitex/_sphinx_html/_modules/index.html new file mode 100644 index 000000000..dd83ddd72 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/index.html @@ -0,0 +1,766 @@ + + + + + + + + Overview: module code — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ +

All modules for which code is available

+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/logging.html b/src/scitex/_sphinx_html/_modules/logging.html new file mode 100644 index 000000000..86f6ad092 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/logging.html @@ -0,0 +1,3019 @@ + + + + + + + + logging — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for logging

+# Copyright 2001-2022 by Vinay Sajip. All Rights Reserved.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose and without fee is hereby granted,
+# provided that the above copyright notice appear in all copies and that
+# both that copyright notice and this permission notice appear in
+# supporting documentation, and that the name of Vinay Sajip
+# not be used in advertising or publicity pertaining to distribution
+# of the software without specific, written prior permission.
+# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""
+Logging package for Python. Based on PEP 282 and comments thereto in
+comp.lang.python.
+
+Copyright (C) 2001-2022 Vinay Sajip. All Rights Reserved.
+
+To use, simply 'import logging' and log away!
+"""
+
+import sys, os, time, io, re, traceback, warnings, weakref, collections.abc
+
+from types import GenericAlias
+from string import Template
+from string import Formatter as StrFormatter
+
+
+__all__ = ['BASIC_FORMAT', 'BufferingFormatter', 'CRITICAL', 'DEBUG', 'ERROR',
+           'FATAL', 'FileHandler', 'Filter', 'Formatter', 'Handler', 'INFO',
+           'LogRecord', 'Logger', 'LoggerAdapter', 'NOTSET', 'NullHandler',
+           'StreamHandler', 'WARN', 'WARNING', 'addLevelName', 'basicConfig',
+           'captureWarnings', 'critical', 'debug', 'disable', 'error',
+           'exception', 'fatal', 'getLevelName', 'getLogger', 'getLoggerClass',
+           'info', 'log', 'makeLogRecord', 'setLoggerClass', 'shutdown',
+           'warn', 'warning', 'getLogRecordFactory', 'setLogRecordFactory',
+           'lastResort', 'raiseExceptions', 'getLevelNamesMapping',
+           'getHandlerByName', 'getHandlerNames']
+
+import threading
+
+__author__  = "Vinay Sajip <vinay_sajip@red-dove.com>"
+__status__  = "production"
+# The following module attributes are no longer updated.
+__version__ = "0.5.1.2"
+__date__    = "07 February 2010"
+
+#---------------------------------------------------------------------------
+#   Miscellaneous module data
+#---------------------------------------------------------------------------
+
+#
+#_startTime is used as the base when calculating the relative time of events
+#
+_startTime = time.time()
+
+#
+#raiseExceptions is used to see if exceptions during handling should be
+#propagated
+#
+raiseExceptions = True
+
+#
+# If you don't want threading information in the log, set this to False
+#
+logThreads = True
+
+#
+# If you don't want multiprocessing information in the log, set this to False
+#
+logMultiprocessing = True
+
+#
+# If you don't want process information in the log, set this to False
+#
+logProcesses = True
+
+#
+# If you don't want asyncio task information in the log, set this to False
+#
+logAsyncioTasks = True
+
+#---------------------------------------------------------------------------
+#   Level related stuff
+#---------------------------------------------------------------------------
+#
+# Default levels and level names, these can be replaced with any positive set
+# of values having corresponding names. There is a pseudo-level, NOTSET, which
+# is only really there as a lower limit for user-defined levels. Handlers and
+# loggers are initialized with NOTSET so that they will log all messages, even
+# at user-defined levels.
+#
+
+CRITICAL = 50
+FATAL = CRITICAL
+ERROR = 40
+WARNING = 30
+WARN = WARNING
+INFO = 20
+DEBUG = 10
+NOTSET = 0
+
+_levelToName = {
+    CRITICAL: 'CRITICAL',
+    ERROR: 'ERROR',
+    WARNING: 'WARNING',
+    INFO: 'INFO',
+    DEBUG: 'DEBUG',
+    NOTSET: 'NOTSET',
+}
+_nameToLevel = {
+    'CRITICAL': CRITICAL,
+    'FATAL': FATAL,
+    'ERROR': ERROR,
+    'WARN': WARNING,
+    'WARNING': WARNING,
+    'INFO': INFO,
+    'DEBUG': DEBUG,
+    'NOTSET': NOTSET,
+}
+
+def getLevelNamesMapping():
+    return _nameToLevel.copy()
+
+def getLevelName(level):
+    """
+    Return the textual or numeric representation of logging level 'level'.
+
+    If the level is one of the predefined levels (CRITICAL, ERROR, WARNING,
+    INFO, DEBUG) then you get the corresponding string. If you have
+    associated levels with names using addLevelName then the name you have
+    associated with 'level' is returned.
+
+    If a numeric value corresponding to one of the defined levels is passed
+    in, the corresponding string representation is returned.
+
+    If a string representation of the level is passed in, the corresponding
+    numeric value is returned.
+
+    If no matching numeric or string value is passed in, the string
+    'Level %s' % level is returned.
+    """
+    # See Issues #22386, #27937 and #29220 for why it's this way
+    result = _levelToName.get(level)
+    if result is not None:
+        return result
+    result = _nameToLevel.get(level)
+    if result is not None:
+        return result
+    return "Level %s" % level
+
+def addLevelName(level, levelName):
+    """
+    Associate 'levelName' with 'level'.
+
+    This is used when converting levels to text during message formatting.
+    """
+    _acquireLock()
+    try:    #unlikely to cause an exception, but you never know...
+        _levelToName[level] = levelName
+        _nameToLevel[levelName] = level
+    finally:
+        _releaseLock()
+
+if hasattr(sys, "_getframe"):
+    currentframe = lambda: sys._getframe(1)
+else: #pragma: no cover
+    def currentframe():
+        """Return the frame object for the caller's stack frame."""
+        try:
+            raise Exception
+        except Exception as exc:
+            return exc.__traceback__.tb_frame.f_back
+
+#
+# _srcfile is used when walking the stack to check when we've got the first
+# caller stack frame, by skipping frames whose filename is that of this
+# module's source. It therefore should contain the filename of this module's
+# source file.
+#
+# Ordinarily we would use __file__ for this, but frozen modules don't always
+# have __file__ set, for some reason (see Issue #21736). Thus, we get the
+# filename from a handy code object from a function defined in this module.
+# (There's no particular reason for picking addLevelName.)
+#
+
+_srcfile = os.path.normcase(addLevelName.__code__.co_filename)
+
+# _srcfile is only used in conjunction with sys._getframe().
+# Setting _srcfile to None will prevent findCaller() from being called. This
+# way, you can avoid the overhead of fetching caller information.
+
+# The following is based on warnings._is_internal_frame. It makes sure that
+# frames of the import mechanism are skipped when logging at module level and
+# using a stacklevel value greater than one.
+def _is_internal_frame(frame):
+    """Signal whether the frame is a CPython or logging module internal."""
+    filename = os.path.normcase(frame.f_code.co_filename)
+    return filename == _srcfile or (
+        "importlib" in filename and "_bootstrap" in filename
+    )
+
+
+def _checkLevel(level):
+    if isinstance(level, int):
+        rv = level
+    elif str(level) == level:
+        if level not in _nameToLevel:
+            raise ValueError("Unknown level: %r" % level)
+        rv = _nameToLevel[level]
+    else:
+        raise TypeError("Level not an integer or a valid string: %r"
+                        % (level,))
+    return rv
+
+#---------------------------------------------------------------------------
+#   Thread-related stuff
+#---------------------------------------------------------------------------
+
+#
+#_lock is used to serialize access to shared data structures in this module.
+#This needs to be an RLock because fileConfig() creates and configures
+#Handlers, and so might arbitrary user threads. Since Handler code updates the
+#shared dictionary _handlers, it needs to acquire the lock. But if configuring,
+#the lock would already have been acquired - so we need an RLock.
+#The same argument applies to Loggers and Manager.loggerDict.
+#
+_lock = threading.RLock()
+
+def _acquireLock():
+    """
+    Acquire the module-level lock for serializing access to shared data.
+
+    This should be released with _releaseLock().
+    """
+    if _lock:
+        _lock.acquire()
+
+def _releaseLock():
+    """
+    Release the module-level lock acquired by calling _acquireLock().
+    """
+    if _lock:
+        _lock.release()
+
+
+# Prevent a held logging lock from blocking a child from logging.
+
+if not hasattr(os, 'register_at_fork'):  # Windows and friends.
+    def _register_at_fork_reinit_lock(instance):
+        pass  # no-op when os.register_at_fork does not exist.
+else:
+    # A collection of instances with a _at_fork_reinit method (logging.Handler)
+    # to be called in the child after forking.  The weakref avoids us keeping
+    # discarded Handler instances alive.
+    _at_fork_reinit_lock_weakset = weakref.WeakSet()
+
+    def _register_at_fork_reinit_lock(instance):
+        _acquireLock()
+        try:
+            _at_fork_reinit_lock_weakset.add(instance)
+        finally:
+            _releaseLock()
+
+    def _after_at_fork_child_reinit_locks():
+        for handler in _at_fork_reinit_lock_weakset:
+            handler._at_fork_reinit()
+
+        # _acquireLock() was called in the parent before forking.
+        # The lock is reinitialized to unlocked state.
+        _lock._at_fork_reinit()
+
+    os.register_at_fork(before=_acquireLock,
+                        after_in_child=_after_at_fork_child_reinit_locks,
+                        after_in_parent=_releaseLock)
+
+
+#---------------------------------------------------------------------------
+#   The logging record
+#---------------------------------------------------------------------------
+
+class LogRecord(object):
+    """
+    A LogRecord instance represents an event being logged.
+
+    LogRecord instances are created every time something is logged. They
+    contain all the information pertinent to the event being logged. The
+    main information passed in is in msg and args, which are combined
+    using str(msg) % args to create the message field of the record. The
+    record also includes information such as when the record was created,
+    the source line where the logging call was made, and any exception
+    information to be logged.
+    """
+    def __init__(self, name, level, pathname, lineno,
+                 msg, args, exc_info, func=None, sinfo=None, **kwargs):
+        """
+        Initialize a logging record with interesting information.
+        """
+        ct = time.time()
+        self.name = name
+        self.msg = msg
+        #
+        # The following statement allows passing of a dictionary as a sole
+        # argument, so that you can do something like
+        #  logging.debug("a %(a)d b %(b)s", {'a':1, 'b':2})
+        # Suggested by Stefan Behnel.
+        # Note that without the test for args[0], we get a problem because
+        # during formatting, we test to see if the arg is present using
+        # 'if self.args:'. If the event being logged is e.g. 'Value is %d'
+        # and if the passed arg fails 'if self.args:' then no formatting
+        # is done. For example, logger.warning('Value is %d', 0) would log
+        # 'Value is %d' instead of 'Value is 0'.
+        # For the use case of passing a dictionary, this should not be a
+        # problem.
+        # Issue #21172: a request was made to relax the isinstance check
+        # to hasattr(args[0], '__getitem__'). However, the docs on string
+        # formatting still seem to suggest a mapping object is required.
+        # Thus, while not removing the isinstance check, it does now look
+        # for collections.abc.Mapping rather than, as before, dict.
+        if (args and len(args) == 1 and isinstance(args[0], collections.abc.Mapping)
+            and args[0]):
+            args = args[0]
+        self.args = args
+        self.levelname = getLevelName(level)
+        self.levelno = level
+        self.pathname = pathname
+        try:
+            self.filename = os.path.basename(pathname)
+            self.module = os.path.splitext(self.filename)[0]
+        except (TypeError, ValueError, AttributeError):
+            self.filename = pathname
+            self.module = "Unknown module"
+        self.exc_info = exc_info
+        self.exc_text = None      # used to cache the traceback text
+        self.stack_info = sinfo
+        self.lineno = lineno
+        self.funcName = func
+        self.created = ct
+        self.msecs = int((ct - int(ct)) * 1000) + 0.0  # see gh-89047
+        self.relativeCreated = (self.created - _startTime) * 1000
+        if logThreads:
+            self.thread = threading.get_ident()
+            self.threadName = threading.current_thread().name
+        else: # pragma: no cover
+            self.thread = None
+            self.threadName = None
+        if not logMultiprocessing: # pragma: no cover
+            self.processName = None
+        else:
+            self.processName = 'MainProcess'
+            mp = sys.modules.get('multiprocessing')
+            if mp is not None:
+                # Errors may occur if multiprocessing has not finished loading
+                # yet - e.g. if a custom import hook causes third-party code
+                # to run when multiprocessing calls import. See issue 8200
+                # for an example
+                try:
+                    self.processName = mp.current_process().name
+                except Exception: #pragma: no cover
+                    pass
+        if logProcesses and hasattr(os, 'getpid'):
+            self.process = os.getpid()
+        else:
+            self.process = None
+
+        self.taskName = None
+        if logAsyncioTasks:
+            asyncio = sys.modules.get('asyncio')
+            if asyncio:
+                try:
+                    self.taskName = asyncio.current_task().get_name()
+                except Exception:
+                    pass
+
+    def __repr__(self):
+        return '<LogRecord: %s, %s, %s, %s, "%s">'%(self.name, self.levelno,
+            self.pathname, self.lineno, self.msg)
+
+    def getMessage(self):
+        """
+        Return the message for this LogRecord.
+
+        Return the message for this LogRecord after merging any user-supplied
+        arguments with the message.
+        """
+        msg = str(self.msg)
+        if self.args:
+            msg = msg % self.args
+        return msg
+
+#
+#   Determine which class to use when instantiating log records.
+#
+_logRecordFactory = LogRecord
+
+def setLogRecordFactory(factory):
+    """
+    Set the factory to be used when instantiating a log record.
+
+    :param factory: A callable which will be called to instantiate
+    a log record.
+    """
+    global _logRecordFactory
+    _logRecordFactory = factory
+
+def getLogRecordFactory():
+    """
+    Return the factory to be used when instantiating a log record.
+    """
+
+    return _logRecordFactory
+
+def makeLogRecord(dict):
+    """
+    Make a LogRecord whose attributes are defined by the specified dictionary,
+    This function is useful for converting a logging event received over
+    a socket connection (which is sent as a dictionary) into a LogRecord
+    instance.
+    """
+    rv = _logRecordFactory(None, None, "", 0, "", (), None, None)
+    rv.__dict__.update(dict)
+    return rv
+
+
+#---------------------------------------------------------------------------
+#   Formatter classes and functions
+#---------------------------------------------------------------------------
+_str_formatter = StrFormatter()
+del StrFormatter
+
+
+class PercentStyle(object):
+
+    default_format = '%(message)s'
+    asctime_format = '%(asctime)s'
+    asctime_search = '%(asctime)'
+    validation_pattern = re.compile(r'%\(\w+\)[#0+ -]*(\*|\d+)?(\.(\*|\d+))?[diouxefgcrsa%]', re.I)
+
+    def __init__(self, fmt, *, defaults=None):
+        self._fmt = fmt or self.default_format
+        self._defaults = defaults
+
+    def usesTime(self):
+        return self._fmt.find(self.asctime_search) >= 0
+
+    def validate(self):
+        """Validate the input format, ensure it matches the correct style"""
+        if not self.validation_pattern.search(self._fmt):
+            raise ValueError("Invalid format '%s' for '%s' style" % (self._fmt, self.default_format[0]))
+
+    def _format(self, record):
+        if defaults := self._defaults:
+            values = defaults | record.__dict__
+        else:
+            values = record.__dict__
+        return self._fmt % values
+
+    def format(self, record):
+        try:
+            return self._format(record)
+        except KeyError as e:
+            raise ValueError('Formatting field not found in record: %s' % e)
+
+
+class StrFormatStyle(PercentStyle):
+    default_format = '{message}'
+    asctime_format = '{asctime}'
+    asctime_search = '{asctime'
+
+    fmt_spec = re.compile(r'^(.?[<>=^])?[+ -]?#?0?(\d+|{\w+})?[,_]?(\.(\d+|{\w+}))?[bcdefgnosx%]?$', re.I)
+    field_spec = re.compile(r'^(\d+|\w+)(\.\w+|\[[^]]+\])*$')
+
+    def _format(self, record):
+        if defaults := self._defaults:
+            values = defaults | record.__dict__
+        else:
+            values = record.__dict__
+        return self._fmt.format(**values)
+
+    def validate(self):
+        """Validate the input format, ensure it is the correct string formatting style"""
+        fields = set()
+        try:
+            for _, fieldname, spec, conversion in _str_formatter.parse(self._fmt):
+                if fieldname:
+                    if not self.field_spec.match(fieldname):
+                        raise ValueError('invalid field name/expression: %r' % fieldname)
+                    fields.add(fieldname)
+                if conversion and conversion not in 'rsa':
+                    raise ValueError('invalid conversion: %r' % conversion)
+                if spec and not self.fmt_spec.match(spec):
+                    raise ValueError('bad specifier: %r' % spec)
+        except ValueError as e:
+            raise ValueError('invalid format: %s' % e)
+        if not fields:
+            raise ValueError('invalid format: no fields')
+
+
+class StringTemplateStyle(PercentStyle):
+    default_format = '${message}'
+    asctime_format = '${asctime}'
+    asctime_search = '${asctime}'
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self._tpl = Template(self._fmt)
+
+    def usesTime(self):
+        fmt = self._fmt
+        return fmt.find('$asctime') >= 0 or fmt.find(self.asctime_search) >= 0
+
+    def validate(self):
+        pattern = Template.pattern
+        fields = set()
+        for m in pattern.finditer(self._fmt):
+            d = m.groupdict()
+            if d['named']:
+                fields.add(d['named'])
+            elif d['braced']:
+                fields.add(d['braced'])
+            elif m.group(0) == '$':
+                raise ValueError('invalid format: bare \'$\' not allowed')
+        if not fields:
+            raise ValueError('invalid format: no fields')
+
+    def _format(self, record):
+        if defaults := self._defaults:
+            values = defaults | record.__dict__
+        else:
+            values = record.__dict__
+        return self._tpl.substitute(**values)
+
+
+BASIC_FORMAT = "%(levelname)s:%(name)s:%(message)s"
+
+_STYLES = {
+    '%': (PercentStyle, BASIC_FORMAT),
+    '{': (StrFormatStyle, '{levelname}:{name}:{message}'),
+    '$': (StringTemplateStyle, '${levelname}:${name}:${message}'),
+}
+
+class Formatter(object):
+    """
+    Formatter instances are used to convert a LogRecord to text.
+
+    Formatters need to know how a LogRecord is constructed. They are
+    responsible for converting a LogRecord to (usually) a string which can
+    be interpreted by either a human or an external system. The base Formatter
+    allows a formatting string to be specified. If none is supplied, the
+    style-dependent default value, "%(message)s", "{message}", or
+    "${message}", is used.
+
+    The Formatter can be initialized with a format string which makes use of
+    knowledge of the LogRecord attributes - e.g. the default value mentioned
+    above makes use of the fact that the user's message and arguments are pre-
+    formatted into a LogRecord's message attribute. Currently, the useful
+    attributes in a LogRecord are described by:
+
+    %(name)s            Name of the logger (logging channel)
+    %(levelno)s         Numeric logging level for the message (DEBUG, INFO,
+                        WARNING, ERROR, CRITICAL)
+    %(levelname)s       Text logging level for the message ("DEBUG", "INFO",
+                        "WARNING", "ERROR", "CRITICAL")
+    %(pathname)s        Full pathname of the source file where the logging
+                        call was issued (if available)
+    %(filename)s        Filename portion of pathname
+    %(module)s          Module (name portion of filename)
+    %(lineno)d          Source line number where the logging call was issued
+                        (if available)
+    %(funcName)s        Function name
+    %(created)f         Time when the LogRecord was created (time.time()
+                        return value)
+    %(asctime)s         Textual time when the LogRecord was created
+    %(msecs)d           Millisecond portion of the creation time
+    %(relativeCreated)d Time in milliseconds when the LogRecord was created,
+                        relative to the time the logging module was loaded
+                        (typically at application startup time)
+    %(thread)d          Thread ID (if available)
+    %(threadName)s      Thread name (if available)
+    %(taskName)s        Task name (if available)
+    %(process)d         Process ID (if available)
+    %(message)s         The result of record.getMessage(), computed just as
+                        the record is emitted
+    """
+
+    converter = time.localtime
+
+    def __init__(self, fmt=None, datefmt=None, style='%', validate=True, *,
+                 defaults=None):
+        """
+        Initialize the formatter with specified format strings.
+
+        Initialize the formatter either with the specified format string, or a
+        default as described above. Allow for specialized date formatting with
+        the optional datefmt argument. If datefmt is omitted, you get an
+        ISO8601-like (or RFC 3339-like) format.
+
+        Use a style parameter of '%', '{' or '$' to specify that you want to
+        use one of %-formatting, :meth:`str.format` (``{}``) formatting or
+        :class:`string.Template` formatting in your format string.
+
+        .. versionchanged:: 3.2
+           Added the ``style`` parameter.
+        """
+        if style not in _STYLES:
+            raise ValueError('Style must be one of: %s' % ','.join(
+                             _STYLES.keys()))
+        self._style = _STYLES[style][0](fmt, defaults=defaults)
+        if validate:
+            self._style.validate()
+
+        self._fmt = self._style._fmt
+        self.datefmt = datefmt
+
+    default_time_format = '%Y-%m-%d %H:%M:%S'
+    default_msec_format = '%s,%03d'
+
+    def formatTime(self, record, datefmt=None):
+        """
+        Return the creation time of the specified LogRecord as formatted text.
+
+        This method should be called from format() by a formatter which
+        wants to make use of a formatted time. This method can be overridden
+        in formatters to provide for any specific requirement, but the
+        basic behaviour is as follows: if datefmt (a string) is specified,
+        it is used with time.strftime() to format the creation time of the
+        record. Otherwise, an ISO8601-like (or RFC 3339-like) format is used.
+        The resulting string is returned. This function uses a user-configurable
+        function to convert the creation time to a tuple. By default,
+        time.localtime() is used; to change this for a particular formatter
+        instance, set the 'converter' attribute to a function with the same
+        signature as time.localtime() or time.gmtime(). To change it for all
+        formatters, for example if you want all logging times to be shown in GMT,
+        set the 'converter' attribute in the Formatter class.
+        """
+        ct = self.converter(record.created)
+        if datefmt:
+            s = time.strftime(datefmt, ct)
+        else:
+            s = time.strftime(self.default_time_format, ct)
+            if self.default_msec_format:
+                s = self.default_msec_format % (s, record.msecs)
+        return s
+
+    def formatException(self, ei):
+        """
+        Format and return the specified exception information as a string.
+
+        This default implementation just uses
+        traceback.print_exception()
+        """
+        sio = io.StringIO()
+        tb = ei[2]
+        # See issues #9427, #1553375. Commented out for now.
+        #if getattr(self, 'fullstack', False):
+        #    traceback.print_stack(tb.tb_frame.f_back, file=sio)
+        traceback.print_exception(ei[0], ei[1], tb, None, sio)
+        s = sio.getvalue()
+        sio.close()
+        if s[-1:] == "\n":
+            s = s[:-1]
+        return s
+
+    def usesTime(self):
+        """
+        Check if the format uses the creation time of the record.
+        """
+        return self._style.usesTime()
+
+    def formatMessage(self, record):
+        return self._style.format(record)
+
+    def formatStack(self, stack_info):
+        """
+        This method is provided as an extension point for specialized
+        formatting of stack information.
+
+        The input data is a string as returned from a call to
+        :func:`traceback.print_stack`, but with the last trailing newline
+        removed.
+
+        The base implementation just returns the value passed in.
+        """
+        return stack_info
+
+    def format(self, record):
+        """
+        Format the specified record as text.
+
+        The record's attribute dictionary is used as the operand to a
+        string formatting operation which yields the returned string.
+        Before formatting the dictionary, a couple of preparatory steps
+        are carried out. The message attribute of the record is computed
+        using LogRecord.getMessage(). If the formatting string uses the
+        time (as determined by a call to usesTime(), formatTime() is
+        called to format the event time. If there is exception information,
+        it is formatted using formatException() and appended to the message.
+        """
+        record.message = record.getMessage()
+        if self.usesTime():
+            record.asctime = self.formatTime(record, self.datefmt)
+        s = self.formatMessage(record)
+        if record.exc_info:
+            # Cache the traceback text to avoid converting it multiple times
+            # (it's constant anyway)
+            if not record.exc_text:
+                record.exc_text = self.formatException(record.exc_info)
+        if record.exc_text:
+            if s[-1:] != "\n":
+                s = s + "\n"
+            s = s + record.exc_text
+        if record.stack_info:
+            if s[-1:] != "\n":
+                s = s + "\n"
+            s = s + self.formatStack(record.stack_info)
+        return s
+
+#
+#   The default formatter to use when no other is specified
+#
+_defaultFormatter = Formatter()
+
+class BufferingFormatter(object):
+    """
+    A formatter suitable for formatting a number of records.
+    """
+    def __init__(self, linefmt=None):
+        """
+        Optionally specify a formatter which will be used to format each
+        individual record.
+        """
+        if linefmt:
+            self.linefmt = linefmt
+        else:
+            self.linefmt = _defaultFormatter
+
+    def formatHeader(self, records):
+        """
+        Return the header string for the specified records.
+        """
+        return ""
+
+    def formatFooter(self, records):
+        """
+        Return the footer string for the specified records.
+        """
+        return ""
+
+    def format(self, records):
+        """
+        Format the specified records and return the result as a string.
+        """
+        rv = ""
+        if len(records) > 0:
+            rv = rv + self.formatHeader(records)
+            for record in records:
+                rv = rv + self.linefmt.format(record)
+            rv = rv + self.formatFooter(records)
+        return rv
+
+#---------------------------------------------------------------------------
+#   Filter classes and functions
+#---------------------------------------------------------------------------
+
+class Filter(object):
+    """
+    Filter instances are used to perform arbitrary filtering of LogRecords.
+
+    Loggers and Handlers can optionally use Filter instances to filter
+    records as desired. The base filter class only allows events which are
+    below a certain point in the logger hierarchy. For example, a filter
+    initialized with "A.B" will allow events logged by loggers "A.B",
+    "A.B.C", "A.B.C.D", "A.B.D" etc. but not "A.BB", "B.A.B" etc. If
+    initialized with the empty string, all events are passed.
+    """
+    def __init__(self, name=''):
+        """
+        Initialize a filter.
+
+        Initialize with the name of the logger which, together with its
+        children, will have its events allowed through the filter. If no
+        name is specified, allow every event.
+        """
+        self.name = name
+        self.nlen = len(name)
+
+    def filter(self, record):
+        """
+        Determine if the specified record is to be logged.
+
+        Returns True if the record should be logged, or False otherwise.
+        If deemed appropriate, the record may be modified in-place.
+        """
+        if self.nlen == 0:
+            return True
+        elif self.name == record.name:
+            return True
+        elif record.name.find(self.name, 0, self.nlen) != 0:
+            return False
+        return (record.name[self.nlen] == ".")
+
+class Filterer(object):
+    """
+    A base class for loggers and handlers which allows them to share
+    common code.
+    """
+    def __init__(self):
+        """
+        Initialize the list of filters to be an empty list.
+        """
+        self.filters = []
+
+    def addFilter(self, filter):
+        """
+        Add the specified filter to this handler.
+        """
+        if not (filter in self.filters):
+            self.filters.append(filter)
+
+    def removeFilter(self, filter):
+        """
+        Remove the specified filter from this handler.
+        """
+        if filter in self.filters:
+            self.filters.remove(filter)
+
+    def filter(self, record):
+        """
+        Determine if a record is loggable by consulting all the filters.
+
+        The default is to allow the record to be logged; any filter can veto
+        this by returning a false value.
+        If a filter attached to a handler returns a log record instance,
+        then that instance is used in place of the original log record in
+        any further processing of the event by that handler.
+        If a filter returns any other true value, the original log record
+        is used in any further processing of the event by that handler.
+
+        If none of the filters return false values, this method returns
+        a log record.
+        If any of the filters return a false value, this method returns
+        a false value.
+
+        .. versionchanged:: 3.2
+
+           Allow filters to be just callables.
+
+        .. versionchanged:: 3.12
+           Allow filters to return a LogRecord instead of
+           modifying it in place.
+        """
+        for f in self.filters:
+            if hasattr(f, 'filter'):
+                result = f.filter(record)
+            else:
+                result = f(record) # assume callable - will raise if not
+            if not result:
+                return False
+            if isinstance(result, LogRecord):
+                record = result
+        return record
+
+#---------------------------------------------------------------------------
+#   Handler classes and functions
+#---------------------------------------------------------------------------
+
+_handlers = weakref.WeakValueDictionary()  #map of handler names to handlers
+_handlerList = [] # added to allow handlers to be removed in reverse of order initialized
+
+def _removeHandlerRef(wr):
+    """
+    Remove a handler reference from the internal cleanup list.
+    """
+    # This function can be called during module teardown, when globals are
+    # set to None. It can also be called from another thread. So we need to
+    # pre-emptively grab the necessary globals and check if they're None,
+    # to prevent race conditions and failures during interpreter shutdown.
+    acquire, release, handlers = _acquireLock, _releaseLock, _handlerList
+    if acquire and release and handlers:
+        acquire()
+        try:
+            handlers.remove(wr)
+        except ValueError:
+            pass
+        finally:
+            release()
+
+def _addHandlerRef(handler):
+    """
+    Add a handler to the internal cleanup list using a weak reference.
+    """
+    _acquireLock()
+    try:
+        _handlerList.append(weakref.ref(handler, _removeHandlerRef))
+    finally:
+        _releaseLock()
+
+
+def getHandlerByName(name):
+    """
+    Get a handler with the specified *name*, or None if there isn't one with
+    that name.
+    """
+    return _handlers.get(name)
+
+
+def getHandlerNames():
+    """
+    Return all known handler names as an immutable set.
+    """
+    result = set(_handlers.keys())
+    return frozenset(result)
+
+
+class Handler(Filterer):
+    """
+    Handler instances dispatch logging events to specific destinations.
+
+    The base handler class. Acts as a placeholder which defines the Handler
+    interface. Handlers can optionally use Formatter instances to format
+    records as desired. By default, no formatter is specified; in this case,
+    the 'raw' message as determined by record.message is logged.
+    """
+    def __init__(self, level=NOTSET):
+        """
+        Initializes the instance - basically setting the formatter to None
+        and the filter list to empty.
+        """
+        Filterer.__init__(self)
+        self._name = None
+        self.level = _checkLevel(level)
+        self.formatter = None
+        self._closed = False
+        # Add the handler to the global _handlerList (for cleanup on shutdown)
+        _addHandlerRef(self)
+        self.createLock()
+
+    def get_name(self):
+        return self._name
+
+    def set_name(self, name):
+        _acquireLock()
+        try:
+            if self._name in _handlers:
+                del _handlers[self._name]
+            self._name = name
+            if name:
+                _handlers[name] = self
+        finally:
+            _releaseLock()
+
+    name = property(get_name, set_name)
+
+    def createLock(self):
+        """
+        Acquire a thread lock for serializing access to the underlying I/O.
+        """
+        self.lock = threading.RLock()
+        _register_at_fork_reinit_lock(self)
+
+    def _at_fork_reinit(self):
+        self.lock._at_fork_reinit()
+
+    def acquire(self):
+        """
+        Acquire the I/O thread lock.
+        """
+        if self.lock:
+            self.lock.acquire()
+
+    def release(self):
+        """
+        Release the I/O thread lock.
+        """
+        if self.lock:
+            self.lock.release()
+
+    def setLevel(self, level):
+        """
+        Set the logging level of this handler.  level must be an int or a str.
+        """
+        self.level = _checkLevel(level)
+
+    def format(self, record):
+        """
+        Format the specified record.
+
+        If a formatter is set, use it. Otherwise, use the default formatter
+        for the module.
+        """
+        if self.formatter:
+            fmt = self.formatter
+        else:
+            fmt = _defaultFormatter
+        return fmt.format(record)
+
+    def emit(self, record):
+        """
+        Do whatever it takes to actually log the specified logging record.
+
+        This version is intended to be implemented by subclasses and so
+        raises a NotImplementedError.
+        """
+        raise NotImplementedError('emit must be implemented '
+                                  'by Handler subclasses')
+
+    def handle(self, record):
+        """
+        Conditionally emit the specified logging record.
+
+        Emission depends on filters which may have been added to the handler.
+        Wrap the actual emission of the record with acquisition/release of
+        the I/O thread lock.
+
+        Returns an instance of the log record that was emitted
+        if it passed all filters, otherwise a false value is returned.
+        """
+        rv = self.filter(record)
+        if isinstance(rv, LogRecord):
+            record = rv
+        if rv:
+            self.acquire()
+            try:
+                self.emit(record)
+            finally:
+                self.release()
+        return rv
+
+    def setFormatter(self, fmt):
+        """
+        Set the formatter for this handler.
+        """
+        self.formatter = fmt
+
+    def flush(self):
+        """
+        Ensure all logging output has been flushed.
+
+        This version does nothing and is intended to be implemented by
+        subclasses.
+        """
+        pass
+
+    def close(self):
+        """
+        Tidy up any resources used by the handler.
+
+        This version removes the handler from an internal map of handlers,
+        _handlers, which is used for handler lookup by name. Subclasses
+        should ensure that this gets called from overridden close()
+        methods.
+        """
+        #get the module data lock, as we're updating a shared structure.
+        _acquireLock()
+        try:    #unlikely to raise an exception, but you never know...
+            self._closed = True
+            if self._name and self._name in _handlers:
+                del _handlers[self._name]
+        finally:
+            _releaseLock()
+
+    def handleError(self, record):
+        """
+        Handle errors which occur during an emit() call.
+
+        This method should be called from handlers when an exception is
+        encountered during an emit() call. If raiseExceptions is false,
+        exceptions get silently ignored. This is what is mostly wanted
+        for a logging system - most users will not care about errors in
+        the logging system, they are more interested in application errors.
+        You could, however, replace this with a custom handler if you wish.
+        The record which was being processed is passed in to this method.
+        """
+        if raiseExceptions and sys.stderr:  # see issue 13807
+            t, v, tb = sys.exc_info()
+            try:
+                sys.stderr.write('--- Logging error ---\n')
+                traceback.print_exception(t, v, tb, None, sys.stderr)
+                sys.stderr.write('Call stack:\n')
+                # Walk the stack frame up until we're out of logging,
+                # so as to print the calling context.
+                frame = tb.tb_frame
+                while (frame and os.path.dirname(frame.f_code.co_filename) ==
+                       __path__[0]):
+                    frame = frame.f_back
+                if frame:
+                    traceback.print_stack(frame, file=sys.stderr)
+                else:
+                    # couldn't find the right stack frame, for some reason
+                    sys.stderr.write('Logged from file %s, line %s\n' % (
+                                     record.filename, record.lineno))
+                # Issue 18671: output logging message and arguments
+                try:
+                    sys.stderr.write('Message: %r\n'
+                                     'Arguments: %s\n' % (record.msg,
+                                                          record.args))
+                except RecursionError:  # See issue 36272
+                    raise
+                except Exception:
+                    sys.stderr.write('Unable to print the message and arguments'
+                                     ' - possible formatting error.\nUse the'
+                                     ' traceback above to help find the error.\n'
+                                    )
+            except OSError: #pragma: no cover
+                pass    # see issue 5971
+            finally:
+                del t, v, tb
+
+    def __repr__(self):
+        level = getLevelName(self.level)
+        return '<%s (%s)>' % (self.__class__.__name__, level)
+
+class StreamHandler(Handler):
+    """
+    A handler class which writes logging records, appropriately formatted,
+    to a stream. Note that this class does not close the stream, as
+    sys.stdout or sys.stderr may be used.
+    """
+
+    terminator = '\n'
+
+    def __init__(self, stream=None):
+        """
+        Initialize the handler.
+
+        If stream is not specified, sys.stderr is used.
+        """
+        Handler.__init__(self)
+        if stream is None:
+            stream = sys.stderr
+        self.stream = stream
+
+    def flush(self):
+        """
+        Flushes the stream.
+        """
+        self.acquire()
+        try:
+            if self.stream and hasattr(self.stream, "flush"):
+                self.stream.flush()
+        finally:
+            self.release()
+
+    def emit(self, record):
+        """
+        Emit a record.
+
+        If a formatter is specified, it is used to format the record.
+        The record is then written to the stream with a trailing newline.  If
+        exception information is present, it is formatted using
+        traceback.print_exception and appended to the stream.  If the stream
+        has an 'encoding' attribute, it is used to determine how to do the
+        output to the stream.
+        """
+        try:
+            msg = self.format(record)
+            stream = self.stream
+            # issue 35046: merged two stream.writes into one.
+            stream.write(msg + self.terminator)
+            self.flush()
+        except RecursionError:  # See issue 36272
+            raise
+        except Exception:
+            self.handleError(record)
+
+    def setStream(self, stream):
+        """
+        Sets the StreamHandler's stream to the specified value,
+        if it is different.
+
+        Returns the old stream, if the stream was changed, or None
+        if it wasn't.
+        """
+        if stream is self.stream:
+            result = None
+        else:
+            result = self.stream
+            self.acquire()
+            try:
+                self.flush()
+                self.stream = stream
+            finally:
+                self.release()
+        return result
+
+    def __repr__(self):
+        level = getLevelName(self.level)
+        name = getattr(self.stream, 'name', '')
+        #  bpo-36015: name can be an int
+        name = str(name)
+        if name:
+            name += ' '
+        return '<%s %s(%s)>' % (self.__class__.__name__, name, level)
+
+    __class_getitem__ = classmethod(GenericAlias)
+
+
+class FileHandler(StreamHandler):
+    """
+    A handler class which writes formatted logging records to disk files.
+    """
+    def __init__(self, filename, mode='a', encoding=None, delay=False, errors=None):
+        """
+        Open the specified file and use it as the stream for logging.
+        """
+        # Issue #27493: add support for Path objects to be passed in
+        filename = os.fspath(filename)
+        #keep the absolute path, otherwise derived classes which use this
+        #may come a cropper when the current directory changes
+        self.baseFilename = os.path.abspath(filename)
+        self.mode = mode
+        self.encoding = encoding
+        if "b" not in mode:
+            self.encoding = io.text_encoding(encoding)
+        self.errors = errors
+        self.delay = delay
+        # bpo-26789: FileHandler keeps a reference to the builtin open()
+        # function to be able to open or reopen the file during Python
+        # finalization.
+        self._builtin_open = open
+        if delay:
+            #We don't open the stream, but we still need to call the
+            #Handler constructor to set level, formatter, lock etc.
+            Handler.__init__(self)
+            self.stream = None
+        else:
+            StreamHandler.__init__(self, self._open())
+
+    def close(self):
+        """
+        Closes the stream.
+        """
+        self.acquire()
+        try:
+            try:
+                if self.stream:
+                    try:
+                        self.flush()
+                    finally:
+                        stream = self.stream
+                        self.stream = None
+                        if hasattr(stream, "close"):
+                            stream.close()
+            finally:
+                # Issue #19523: call unconditionally to
+                # prevent a handler leak when delay is set
+                # Also see Issue #42378: we also rely on
+                # self._closed being set to True there
+                StreamHandler.close(self)
+        finally:
+            self.release()
+
+    def _open(self):
+        """
+        Open the current base file with the (original) mode and encoding.
+        Return the resulting stream.
+        """
+        open_func = self._builtin_open
+        return open_func(self.baseFilename, self.mode,
+                         encoding=self.encoding, errors=self.errors)
+
+    def emit(self, record):
+        """
+        Emit a record.
+
+        If the stream was not opened because 'delay' was specified in the
+        constructor, open it before calling the superclass's emit.
+
+        If stream is not open, current mode is 'w' and `_closed=True`, record
+        will not be emitted (see Issue #42378).
+        """
+        if self.stream is None:
+            if self.mode != 'w' or not self._closed:
+                self.stream = self._open()
+        if self.stream:
+            StreamHandler.emit(self, record)
+
+    def __repr__(self):
+        level = getLevelName(self.level)
+        return '<%s %s (%s)>' % (self.__class__.__name__, self.baseFilename, level)
+
+
+class _StderrHandler(StreamHandler):
+    """
+    This class is like a StreamHandler using sys.stderr, but always uses
+    whatever sys.stderr is currently set to rather than the value of
+    sys.stderr at handler construction time.
+    """
+    def __init__(self, level=NOTSET):
+        """
+        Initialize the handler.
+        """
+        Handler.__init__(self, level)
+
+    @property
+    def stream(self):
+        return sys.stderr
+
+
+_defaultLastResort = _StderrHandler(WARNING)
+lastResort = _defaultLastResort
+
+#---------------------------------------------------------------------------
+#   Manager classes and functions
+#---------------------------------------------------------------------------
+
+class PlaceHolder(object):
+    """
+    PlaceHolder instances are used in the Manager logger hierarchy to take
+    the place of nodes for which no loggers have been defined. This class is
+    intended for internal use only and not as part of the public API.
+    """
+    def __init__(self, alogger):
+        """
+        Initialize with the specified logger being a child of this placeholder.
+        """
+        self.loggerMap = { alogger : None }
+
+    def append(self, alogger):
+        """
+        Add the specified logger as a child of this placeholder.
+        """
+        if alogger not in self.loggerMap:
+            self.loggerMap[alogger] = None
+
+#
+#   Determine which class to use when instantiating loggers.
+#
+
+def setLoggerClass(klass):
+    """
+    Set the class to be used when instantiating a logger. The class should
+    define __init__() such that only a name argument is required, and the
+    __init__() should call Logger.__init__()
+    """
+    if klass != Logger:
+        if not issubclass(klass, Logger):
+            raise TypeError("logger not derived from logging.Logger: "
+                            + klass.__name__)
+    global _loggerClass
+    _loggerClass = klass
+
+def getLoggerClass():
+    """
+    Return the class to be used when instantiating a logger.
+    """
+    return _loggerClass
+
+class Manager(object):
+    """
+    There is [under normal circumstances] just one Manager instance, which
+    holds the hierarchy of loggers.
+    """
+    def __init__(self, rootnode):
+        """
+        Initialize the manager with the root node of the logger hierarchy.
+        """
+        self.root = rootnode
+        self.disable = 0
+        self.emittedNoHandlerWarning = False
+        self.loggerDict = {}
+        self.loggerClass = None
+        self.logRecordFactory = None
+
+    @property
+    def disable(self):
+        return self._disable
+
+    @disable.setter
+    def disable(self, value):
+        self._disable = _checkLevel(value)
+
+    def getLogger(self, name):
+        """
+        Get a logger with the specified name (channel name), creating it
+        if it doesn't yet exist. This name is a dot-separated hierarchical
+        name, such as "a", "a.b", "a.b.c" or similar.
+
+        If a PlaceHolder existed for the specified name [i.e. the logger
+        didn't exist but a child of it did], replace it with the created
+        logger and fix up the parent/child references which pointed to the
+        placeholder to now point to the logger.
+        """
+        rv = None
+        if not isinstance(name, str):
+            raise TypeError('A logger name must be a string')
+        _acquireLock()
+        try:
+            if name in self.loggerDict:
+                rv = self.loggerDict[name]
+                if isinstance(rv, PlaceHolder):
+                    ph = rv
+                    rv = (self.loggerClass or _loggerClass)(name)
+                    rv.manager = self
+                    self.loggerDict[name] = rv
+                    self._fixupChildren(ph, rv)
+                    self._fixupParents(rv)
+            else:
+                rv = (self.loggerClass or _loggerClass)(name)
+                rv.manager = self
+                self.loggerDict[name] = rv
+                self._fixupParents(rv)
+        finally:
+            _releaseLock()
+        return rv
+
+    def setLoggerClass(self, klass):
+        """
+        Set the class to be used when instantiating a logger with this Manager.
+        """
+        if klass != Logger:
+            if not issubclass(klass, Logger):
+                raise TypeError("logger not derived from logging.Logger: "
+                                + klass.__name__)
+        self.loggerClass = klass
+
+    def setLogRecordFactory(self, factory):
+        """
+        Set the factory to be used when instantiating a log record with this
+        Manager.
+        """
+        self.logRecordFactory = factory
+
+    def _fixupParents(self, alogger):
+        """
+        Ensure that there are either loggers or placeholders all the way
+        from the specified logger to the root of the logger hierarchy.
+        """
+        name = alogger.name
+        i = name.rfind(".")
+        rv = None
+        while (i > 0) and not rv:
+            substr = name[:i]
+            if substr not in self.loggerDict:
+                self.loggerDict[substr] = PlaceHolder(alogger)
+            else:
+                obj = self.loggerDict[substr]
+                if isinstance(obj, Logger):
+                    rv = obj
+                else:
+                    assert isinstance(obj, PlaceHolder)
+                    obj.append(alogger)
+            i = name.rfind(".", 0, i - 1)
+        if not rv:
+            rv = self.root
+        alogger.parent = rv
+
+    def _fixupChildren(self, ph, alogger):
+        """
+        Ensure that children of the placeholder ph are connected to the
+        specified logger.
+        """
+        name = alogger.name
+        namelen = len(name)
+        for c in ph.loggerMap.keys():
+            #The if means ... if not c.parent.name.startswith(nm)
+            if c.parent.name[:namelen] != name:
+                alogger.parent = c.parent
+                c.parent = alogger
+
+    def _clear_cache(self):
+        """
+        Clear the cache for all loggers in loggerDict
+        Called when level changes are made
+        """
+
+        _acquireLock()
+        for logger in self.loggerDict.values():
+            if isinstance(logger, Logger):
+                logger._cache.clear()
+        self.root._cache.clear()
+        _releaseLock()
+
+#---------------------------------------------------------------------------
+#   Logger classes and functions
+#---------------------------------------------------------------------------
+
+class Logger(Filterer):
+    """
+    Instances of the Logger class represent a single logging channel. A
+    "logging channel" indicates an area of an application. Exactly how an
+    "area" is defined is up to the application developer. Since an
+    application can have any number of areas, logging channels are identified
+    by a unique string. Application areas can be nested (e.g. an area
+    of "input processing" might include sub-areas "read CSV files", "read
+    XLS files" and "read Gnumeric files"). To cater for this natural nesting,
+    channel names are organized into a namespace hierarchy where levels are
+    separated by periods, much like the Java or Python package namespace. So
+    in the instance given above, channel names might be "input" for the upper
+    level, and "input.csv", "input.xls" and "input.gnu" for the sub-levels.
+    There is no arbitrary limit to the depth of nesting.
+    """
+    def __init__(self, name, level=NOTSET):
+        """
+        Initialize the logger with a name and an optional level.
+        """
+        Filterer.__init__(self)
+        self.name = name
+        self.level = _checkLevel(level)
+        self.parent = None
+        self.propagate = True
+        self.handlers = []
+        self.disabled = False
+        self._cache = {}
+
+    def setLevel(self, level):
+        """
+        Set the logging level of this logger.  level must be an int or a str.
+        """
+        self.level = _checkLevel(level)
+        self.manager._clear_cache()
+
+    def debug(self, msg, *args, **kwargs):
+        """
+        Log 'msg % args' with severity 'DEBUG'.
+
+        To pass exception information, use the keyword argument exc_info with
+        a true value, e.g.
+
+        logger.debug("Houston, we have a %s", "thorny problem", exc_info=True)
+        """
+        if self.isEnabledFor(DEBUG):
+            self._log(DEBUG, msg, args, **kwargs)
+
+    def info(self, msg, *args, **kwargs):
+        """
+        Log 'msg % args' with severity 'INFO'.
+
+        To pass exception information, use the keyword argument exc_info with
+        a true value, e.g.
+
+        logger.info("Houston, we have a %s", "notable problem", exc_info=True)
+        """
+        if self.isEnabledFor(INFO):
+            self._log(INFO, msg, args, **kwargs)
+
+    def warning(self, msg, *args, **kwargs):
+        """
+        Log 'msg % args' with severity 'WARNING'.
+
+        To pass exception information, use the keyword argument exc_info with
+        a true value, e.g.
+
+        logger.warning("Houston, we have a %s", "bit of a problem", exc_info=True)
+        """
+        if self.isEnabledFor(WARNING):
+            self._log(WARNING, msg, args, **kwargs)
+
+    def warn(self, msg, *args, **kwargs):
+        warnings.warn("The 'warn' method is deprecated, "
+            "use 'warning' instead", DeprecationWarning, 2)
+        self.warning(msg, *args, **kwargs)
+
+    def error(self, msg, *args, **kwargs):
+        """
+        Log 'msg % args' with severity 'ERROR'.
+
+        To pass exception information, use the keyword argument exc_info with
+        a true value, e.g.
+
+        logger.error("Houston, we have a %s", "major problem", exc_info=True)
+        """
+        if self.isEnabledFor(ERROR):
+            self._log(ERROR, msg, args, **kwargs)
+
+    def exception(self, msg, *args, exc_info=True, **kwargs):
+        """
+        Convenience method for logging an ERROR with exception information.
+        """
+        self.error(msg, *args, exc_info=exc_info, **kwargs)
+
+    def critical(self, msg, *args, **kwargs):
+        """
+        Log 'msg % args' with severity 'CRITICAL'.
+
+        To pass exception information, use the keyword argument exc_info with
+        a true value, e.g.
+
+        logger.critical("Houston, we have a %s", "major disaster", exc_info=True)
+        """
+        if self.isEnabledFor(CRITICAL):
+            self._log(CRITICAL, msg, args, **kwargs)
+
+    def fatal(self, msg, *args, **kwargs):
+        """
+        Don't use this method, use critical() instead.
+        """
+        self.critical(msg, *args, **kwargs)
+
+    def log(self, level, msg, *args, **kwargs):
+        """
+        Log 'msg % args' with the integer severity 'level'.
+
+        To pass exception information, use the keyword argument exc_info with
+        a true value, e.g.
+
+        logger.log(level, "We have a %s", "mysterious problem", exc_info=True)
+        """
+        if not isinstance(level, int):
+            if raiseExceptions:
+                raise TypeError("level must be an integer")
+            else:
+                return
+        if self.isEnabledFor(level):
+            self._log(level, msg, args, **kwargs)
+
+    def findCaller(self, stack_info=False, stacklevel=1):
+        """
+        Find the stack frame of the caller so that we can note the source
+        file name, line number and function name.
+        """
+        f = currentframe()
+        #On some versions of IronPython, currentframe() returns None if
+        #IronPython isn't run with -X:Frames.
+        if f is None:
+            return "(unknown file)", 0, "(unknown function)", None
+        while stacklevel > 0:
+            next_f = f.f_back
+            if next_f is None:
+                ## We've got options here.
+                ## If we want to use the last (deepest) frame:
+                break
+                ## If we want to mimic the warnings module:
+                #return ("sys", 1, "(unknown function)", None)
+                ## If we want to be pedantic:
+                #raise ValueError("call stack is not deep enough")
+            f = next_f
+            if not _is_internal_frame(f):
+                stacklevel -= 1
+        co = f.f_code
+        sinfo = None
+        if stack_info:
+            with io.StringIO() as sio:
+                sio.write("Stack (most recent call last):\n")
+                traceback.print_stack(f, file=sio)
+                sinfo = sio.getvalue()
+                if sinfo[-1] == '\n':
+                    sinfo = sinfo[:-1]
+        return co.co_filename, f.f_lineno, co.co_name, sinfo
+
+    def makeRecord(self, name, level, fn, lno, msg, args, exc_info,
+                   func=None, extra=None, sinfo=None):
+        """
+        A factory method which can be overridden in subclasses to create
+        specialized LogRecords.
+        """
+        rv = _logRecordFactory(name, level, fn, lno, msg, args, exc_info, func,
+                             sinfo)
+        if extra is not None:
+            for key in extra:
+                if (key in ["message", "asctime"]) or (key in rv.__dict__):
+                    raise KeyError("Attempt to overwrite %r in LogRecord" % key)
+                rv.__dict__[key] = extra[key]
+        return rv
+
+    def _log(self, level, msg, args, exc_info=None, extra=None, stack_info=False,
+             stacklevel=1):
+        """
+        Low-level logging routine which creates a LogRecord and then calls
+        all the handlers of this logger to handle the record.
+        """
+        sinfo = None
+        if _srcfile:
+            #IronPython doesn't track Python frames, so findCaller raises an
+            #exception on some versions of IronPython. We trap it here so that
+            #IronPython can use logging.
+            try:
+                fn, lno, func, sinfo = self.findCaller(stack_info, stacklevel)
+            except ValueError: # pragma: no cover
+                fn, lno, func = "(unknown file)", 0, "(unknown function)"
+        else: # pragma: no cover
+            fn, lno, func = "(unknown file)", 0, "(unknown function)"
+        if exc_info:
+            if isinstance(exc_info, BaseException):
+                exc_info = (type(exc_info), exc_info, exc_info.__traceback__)
+            elif not isinstance(exc_info, tuple):
+                exc_info = sys.exc_info()
+        record = self.makeRecord(self.name, level, fn, lno, msg, args,
+                                 exc_info, func, extra, sinfo)
+        self.handle(record)
+
+    def handle(self, record):
+        """
+        Call the handlers for the specified record.
+
+        This method is used for unpickled records received from a socket, as
+        well as those created locally. Logger-level filtering is applied.
+        """
+        if self.disabled:
+            return
+        maybe_record = self.filter(record)
+        if not maybe_record:
+            return
+        if isinstance(maybe_record, LogRecord):
+            record = maybe_record
+        self.callHandlers(record)
+
+    def addHandler(self, hdlr):
+        """
+        Add the specified handler to this logger.
+        """
+        _acquireLock()
+        try:
+            if not (hdlr in self.handlers):
+                self.handlers.append(hdlr)
+        finally:
+            _releaseLock()
+
+    def removeHandler(self, hdlr):
+        """
+        Remove the specified handler from this logger.
+        """
+        _acquireLock()
+        try:
+            if hdlr in self.handlers:
+                self.handlers.remove(hdlr)
+        finally:
+            _releaseLock()
+
+    def hasHandlers(self):
+        """
+        See if this logger has any handlers configured.
+
+        Loop through all handlers for this logger and its parents in the
+        logger hierarchy. Return True if a handler was found, else False.
+        Stop searching up the hierarchy whenever a logger with the "propagate"
+        attribute set to zero is found - that will be the last logger which
+        is checked for the existence of handlers.
+        """
+        c = self
+        rv = False
+        while c:
+            if c.handlers:
+                rv = True
+                break
+            if not c.propagate:
+                break
+            else:
+                c = c.parent
+        return rv
+
+    def callHandlers(self, record):
+        """
+        Pass a record to all relevant handlers.
+
+        Loop through all handlers for this logger and its parents in the
+        logger hierarchy. If no handler was found, output a one-off error
+        message to sys.stderr. Stop searching up the hierarchy whenever a
+        logger with the "propagate" attribute set to zero is found - that
+        will be the last logger whose handlers are called.
+        """
+        c = self
+        found = 0
+        while c:
+            for hdlr in c.handlers:
+                found = found + 1
+                if record.levelno >= hdlr.level:
+                    hdlr.handle(record)
+            if not c.propagate:
+                c = None    #break out
+            else:
+                c = c.parent
+        if (found == 0):
+            if lastResort:
+                if record.levelno >= lastResort.level:
+                    lastResort.handle(record)
+            elif raiseExceptions and not self.manager.emittedNoHandlerWarning:
+                sys.stderr.write("No handlers could be found for logger"
+                                 " \"%s\"\n" % self.name)
+                self.manager.emittedNoHandlerWarning = True
+
+    def getEffectiveLevel(self):
+        """
+        Get the effective level for this logger.
+
+        Loop through this logger and its parents in the logger hierarchy,
+        looking for a non-zero logging level. Return the first one found.
+        """
+        logger = self
+        while logger:
+            if logger.level:
+                return logger.level
+            logger = logger.parent
+        return NOTSET
+
+    def isEnabledFor(self, level):
+        """
+        Is this logger enabled for level 'level'?
+        """
+        if self.disabled:
+            return False
+
+        try:
+            return self._cache[level]
+        except KeyError:
+            _acquireLock()
+            try:
+                if self.manager.disable >= level:
+                    is_enabled = self._cache[level] = False
+                else:
+                    is_enabled = self._cache[level] = (
+                        level >= self.getEffectiveLevel()
+                    )
+            finally:
+                _releaseLock()
+            return is_enabled
+
+    def getChild(self, suffix):
+        """
+        Get a logger which is a descendant to this one.
+
+        This is a convenience method, such that
+
+        logging.getLogger('abc').getChild('def.ghi')
+
+        is the same as
+
+        logging.getLogger('abc.def.ghi')
+
+        It's useful, for example, when the parent logger is named using
+        __name__ rather than a literal string.
+        """
+        if self.root is not self:
+            suffix = '.'.join((self.name, suffix))
+        return self.manager.getLogger(suffix)
+
+    def getChildren(self):
+
+        def _hierlevel(logger):
+            if logger is logger.manager.root:
+                return 0
+            return 1 + logger.name.count('.')
+
+        d = self.manager.loggerDict
+        _acquireLock()
+        try:
+            # exclude PlaceHolders - the last check is to ensure that lower-level
+            # descendants aren't returned - if there are placeholders, a logger's
+            # parent field might point to a grandparent or ancestor thereof.
+            return set(item for item in d.values()
+                       if isinstance(item, Logger) and item.parent is self and
+                       _hierlevel(item) == 1 + _hierlevel(item.parent))
+        finally:
+            _releaseLock()
+
+    def __repr__(self):
+        level = getLevelName(self.getEffectiveLevel())
+        return '<%s %s (%s)>' % (self.__class__.__name__, self.name, level)
+
+    def __reduce__(self):
+        if getLogger(self.name) is not self:
+            import pickle
+            raise pickle.PicklingError('logger cannot be pickled')
+        return getLogger, (self.name,)
+
+
+class RootLogger(Logger):
+    """
+    A root logger is not that different to any other logger, except that
+    it must have a logging level and there is only one instance of it in
+    the hierarchy.
+    """
+    def __init__(self, level):
+        """
+        Initialize the logger with the name "root".
+        """
+        Logger.__init__(self, "root", level)
+
+    def __reduce__(self):
+        return getLogger, ()
+
+_loggerClass = Logger
+
+class LoggerAdapter(object):
+    """
+    An adapter for loggers which makes it easier to specify contextual
+    information in logging output.
+    """
+
+    def __init__(self, logger, extra=None):
+        """
+        Initialize the adapter with a logger and a dict-like object which
+        provides contextual information. This constructor signature allows
+        easy stacking of LoggerAdapters, if so desired.
+
+        You can effectively pass keyword arguments as shown in the
+        following example:
+
+        adapter = LoggerAdapter(someLogger, dict(p1=v1, p2="v2"))
+        """
+        self.logger = logger
+        self.extra = extra
+
+    def process(self, msg, kwargs):
+        """
+        Process the logging message and keyword arguments passed in to
+        a logging call to insert contextual information. You can either
+        manipulate the message itself, the keyword args or both. Return
+        the message and kwargs modified (or not) to suit your needs.
+
+        Normally, you'll only need to override this one method in a
+        LoggerAdapter subclass for your specific needs.
+        """
+        kwargs["extra"] = self.extra
+        return msg, kwargs
+
+    #
+    # Boilerplate convenience methods
+    #
+    def debug(self, msg, *args, **kwargs):
+        """
+        Delegate a debug call to the underlying logger.
+        """
+        self.log(DEBUG, msg, *args, **kwargs)
+
+    def info(self, msg, *args, **kwargs):
+        """
+        Delegate an info call to the underlying logger.
+        """
+        self.log(INFO, msg, *args, **kwargs)
+
+    def warning(self, msg, *args, **kwargs):
+        """
+        Delegate a warning call to the underlying logger.
+        """
+        self.log(WARNING, msg, *args, **kwargs)
+
+    def warn(self, msg, *args, **kwargs):
+        warnings.warn("The 'warn' method is deprecated, "
+            "use 'warning' instead", DeprecationWarning, 2)
+        self.warning(msg, *args, **kwargs)
+
+    def error(self, msg, *args, **kwargs):
+        """
+        Delegate an error call to the underlying logger.
+        """
+        self.log(ERROR, msg, *args, **kwargs)
+
+    def exception(self, msg, *args, exc_info=True, **kwargs):
+        """
+        Delegate an exception call to the underlying logger.
+        """
+        self.log(ERROR, msg, *args, exc_info=exc_info, **kwargs)
+
+    def critical(self, msg, *args, **kwargs):
+        """
+        Delegate a critical call to the underlying logger.
+        """
+        self.log(CRITICAL, msg, *args, **kwargs)
+
+    def log(self, level, msg, *args, **kwargs):
+        """
+        Delegate a log call to the underlying logger, after adding
+        contextual information from this adapter instance.
+        """
+        if self.isEnabledFor(level):
+            msg, kwargs = self.process(msg, kwargs)
+            self.logger.log(level, msg, *args, **kwargs)
+
+    def isEnabledFor(self, level):
+        """
+        Is this logger enabled for level 'level'?
+        """
+        return self.logger.isEnabledFor(level)
+
+    def setLevel(self, level):
+        """
+        Set the specified level on the underlying logger.
+        """
+        self.logger.setLevel(level)
+
+    def getEffectiveLevel(self):
+        """
+        Get the effective level for the underlying logger.
+        """
+        return self.logger.getEffectiveLevel()
+
+    def hasHandlers(self):
+        """
+        See if the underlying logger has any handlers.
+        """
+        return self.logger.hasHandlers()
+
+    def _log(self, level, msg, args, **kwargs):
+        """
+        Low-level log implementation, proxied to allow nested logger adapters.
+        """
+        return self.logger._log(level, msg, args, **kwargs)
+
+    @property
+    def manager(self):
+        return self.logger.manager
+
+    @manager.setter
+    def manager(self, value):
+        self.logger.manager = value
+
+    @property
+    def name(self):
+        return self.logger.name
+
+    def __repr__(self):
+        logger = self.logger
+        level = getLevelName(logger.getEffectiveLevel())
+        return '<%s %s (%s)>' % (self.__class__.__name__, logger.name, level)
+
+    __class_getitem__ = classmethod(GenericAlias)
+
+root = RootLogger(WARNING)
+Logger.root = root
+Logger.manager = Manager(Logger.root)
+
+#---------------------------------------------------------------------------
+# Configuration classes and functions
+#---------------------------------------------------------------------------
+
+def basicConfig(**kwargs):
+    """
+    Do basic configuration for the logging system.
+
+    This function does nothing if the root logger already has handlers
+    configured, unless the keyword argument *force* is set to ``True``.
+    It is a convenience method intended for use by simple scripts
+    to do one-shot configuration of the logging package.
+
+    The default behaviour is to create a StreamHandler which writes to
+    sys.stderr, set a formatter using the BASIC_FORMAT format string, and
+    add the handler to the root logger.
+
+    A number of optional keyword arguments may be specified, which can alter
+    the default behaviour.
+
+    filename  Specifies that a FileHandler be created, using the specified
+              filename, rather than a StreamHandler.
+    filemode  Specifies the mode to open the file, if filename is specified
+              (if filemode is unspecified, it defaults to 'a').
+    format    Use the specified format string for the handler.
+    datefmt   Use the specified date/time format.
+    style     If a format string is specified, use this to specify the
+              type of format string (possible values '%', '{', '$', for
+              %-formatting, :meth:`str.format` and :class:`string.Template`
+              - defaults to '%').
+    level     Set the root logger level to the specified level.
+    stream    Use the specified stream to initialize the StreamHandler. Note
+              that this argument is incompatible with 'filename' - if both
+              are present, 'stream' is ignored.
+    handlers  If specified, this should be an iterable of already created
+              handlers, which will be added to the root logger. Any handler
+              in the list which does not have a formatter assigned will be
+              assigned the formatter created in this function.
+    force     If this keyword  is specified as true, any existing handlers
+              attached to the root logger are removed and closed, before
+              carrying out the configuration as specified by the other
+              arguments.
+    encoding  If specified together with a filename, this encoding is passed to
+              the created FileHandler, causing it to be used when the file is
+              opened.
+    errors    If specified together with a filename, this value is passed to the
+              created FileHandler, causing it to be used when the file is
+              opened in text mode. If not specified, the default value is
+              `backslashreplace`.
+
+    Note that you could specify a stream created using open(filename, mode)
+    rather than passing the filename and mode in. However, it should be
+    remembered that StreamHandler does not close its stream (since it may be
+    using sys.stdout or sys.stderr), whereas FileHandler closes its stream
+    when the handler is closed.
+
+    .. versionchanged:: 3.2
+       Added the ``style`` parameter.
+
+    .. versionchanged:: 3.3
+       Added the ``handlers`` parameter. A ``ValueError`` is now thrown for
+       incompatible arguments (e.g. ``handlers`` specified together with
+       ``filename``/``filemode``, or ``filename``/``filemode`` specified
+       together with ``stream``, or ``handlers`` specified together with
+       ``stream``.
+
+    .. versionchanged:: 3.8
+       Added the ``force`` parameter.
+
+    .. versionchanged:: 3.9
+       Added the ``encoding`` and ``errors`` parameters.
+    """
+    # Add thread safety in case someone mistakenly calls
+    # basicConfig() from multiple threads
+    _acquireLock()
+    try:
+        force = kwargs.pop('force', False)
+        encoding = kwargs.pop('encoding', None)
+        errors = kwargs.pop('errors', 'backslashreplace')
+        if force:
+            for h in root.handlers[:]:
+                root.removeHandler(h)
+                h.close()
+        if len(root.handlers) == 0:
+            handlers = kwargs.pop("handlers", None)
+            if handlers is None:
+                if "stream" in kwargs and "filename" in kwargs:
+                    raise ValueError("'stream' and 'filename' should not be "
+                                     "specified together")
+            else:
+                if "stream" in kwargs or "filename" in kwargs:
+                    raise ValueError("'stream' or 'filename' should not be "
+                                     "specified together with 'handlers'")
+            if handlers is None:
+                filename = kwargs.pop("filename", None)
+                mode = kwargs.pop("filemode", 'a')
+                if filename:
+                    if 'b' in mode:
+                        errors = None
+                    else:
+                        encoding = io.text_encoding(encoding)
+                    h = FileHandler(filename, mode,
+                                    encoding=encoding, errors=errors)
+                else:
+                    stream = kwargs.pop("stream", None)
+                    h = StreamHandler(stream)
+                handlers = [h]
+            dfs = kwargs.pop("datefmt", None)
+            style = kwargs.pop("style", '%')
+            if style not in _STYLES:
+                raise ValueError('Style must be one of: %s' % ','.join(
+                                 _STYLES.keys()))
+            fs = kwargs.pop("format", _STYLES[style][1])
+            fmt = Formatter(fs, dfs, style)
+            for h in handlers:
+                if h.formatter is None:
+                    h.setFormatter(fmt)
+                root.addHandler(h)
+            level = kwargs.pop("level", None)
+            if level is not None:
+                root.setLevel(level)
+            if kwargs:
+                keys = ', '.join(kwargs.keys())
+                raise ValueError('Unrecognised argument(s): %s' % keys)
+    finally:
+        _releaseLock()
+
+#---------------------------------------------------------------------------
+# Utility functions at module level.
+# Basically delegate everything to the root logger.
+#---------------------------------------------------------------------------
+
+
+[docs] +def getLogger(name=None): + """ + Return a logger with the specified name, creating it if necessary. + + If no name is specified, return the root logger. + """ + if not name or isinstance(name, str) and name == root.name: + return root + return Logger.manager.getLogger(name)
+ + +def critical(msg, *args, **kwargs): + """ + Log a message with severity 'CRITICAL' on the root logger. If the logger + has no handlers, call basicConfig() to add a console handler with a + pre-defined format. + """ + if len(root.handlers) == 0: + basicConfig() + root.critical(msg, *args, **kwargs) + +def fatal(msg, *args, **kwargs): + """ + Don't use this function, use critical() instead. + """ + critical(msg, *args, **kwargs) + +def error(msg, *args, **kwargs): + """ + Log a message with severity 'ERROR' on the root logger. If the logger has + no handlers, call basicConfig() to add a console handler with a pre-defined + format. + """ + if len(root.handlers) == 0: + basicConfig() + root.error(msg, *args, **kwargs) + +def exception(msg, *args, exc_info=True, **kwargs): + """ + Log a message with severity 'ERROR' on the root logger, with exception + information. If the logger has no handlers, basicConfig() is called to add + a console handler with a pre-defined format. + """ + error(msg, *args, exc_info=exc_info, **kwargs) + +def warning(msg, *args, **kwargs): + """ + Log a message with severity 'WARNING' on the root logger. If the logger has + no handlers, call basicConfig() to add a console handler with a pre-defined + format. + """ + if len(root.handlers) == 0: + basicConfig() + root.warning(msg, *args, **kwargs) + +def warn(msg, *args, **kwargs): + warnings.warn("The 'warn' function is deprecated, " + "use 'warning' instead", DeprecationWarning, 2) + warning(msg, *args, **kwargs) + +def info(msg, *args, **kwargs): + """ + Log a message with severity 'INFO' on the root logger. If the logger has + no handlers, call basicConfig() to add a console handler with a pre-defined + format. + """ + if len(root.handlers) == 0: + basicConfig() + root.info(msg, *args, **kwargs) + +def debug(msg, *args, **kwargs): + """ + Log a message with severity 'DEBUG' on the root logger. If the logger has + no handlers, call basicConfig() to add a console handler with a pre-defined + format. + """ + if len(root.handlers) == 0: + basicConfig() + root.debug(msg, *args, **kwargs) + +def log(level, msg, *args, **kwargs): + """ + Log 'msg % args' with the integer severity 'level' on the root logger. If + the logger has no handlers, call basicConfig() to add a console handler + with a pre-defined format. + """ + if len(root.handlers) == 0: + basicConfig() + root.log(level, msg, *args, **kwargs) + +def disable(level=CRITICAL): + """ + Disable all logging calls of severity 'level' and below. + """ + root.manager.disable = level + root.manager._clear_cache() + +def shutdown(handlerList=_handlerList): + """ + Perform any cleanup actions in the logging system (e.g. flushing + buffers). + + Should be called at application exit. + """ + for wr in reversed(handlerList[:]): + #errors might occur, for example, if files are locked + #we just ignore them if raiseExceptions is not set + try: + h = wr() + if h: + try: + h.acquire() + # MemoryHandlers might not want to be flushed on close, + # but circular imports prevent us scoping this to just + # those handlers. hence the default to True. + if getattr(h, 'flushOnClose', True): + h.flush() + h.close() + except (OSError, ValueError): + # Ignore errors which might be caused + # because handlers have been closed but + # references to them are still around at + # application exit. + pass + finally: + h.release() + except: # ignore everything, as we're shutting down + if raiseExceptions: + raise + #else, swallow + +#Let's try and shutdown automatically on application exit... +import atexit +atexit.register(shutdown) + +# Null handler + +class NullHandler(Handler): + """ + This handler does nothing. It's intended to be used to avoid the + "No handlers could be found for logger XXX" one-off warning. This is + important for library code, which may contain code to log events. If a user + of the library does not configure logging, the one-off warning might be + produced; to avoid this, the library developer simply needs to instantiate + a NullHandler and add it to the top-level logger of the library module or + package. + """ + def handle(self, record): + """Stub.""" + + def emit(self, record): + """Stub.""" + + def createLock(self): + self.lock = None + + def _at_fork_reinit(self): + pass + +# Warnings integration + +_warnings_showwarning = None + +def _showwarning(message, category, filename, lineno, file=None, line=None): + """ + Implementation of showwarnings which redirects to logging, which will first + check to see if the file parameter is None. If a file is specified, it will + delegate to the original warnings implementation of showwarning. Otherwise, + it will call warnings.formatwarning and will log the resulting string to a + warnings logger named "py.warnings" with level logging.WARNING. + """ + if file is not None: + if _warnings_showwarning is not None: + _warnings_showwarning(message, category, filename, lineno, file, line) + else: + s = warnings.formatwarning(message, category, filename, lineno, line) + logger = getLogger("py.warnings") + if not logger.handlers: + logger.addHandler(NullHandler()) + # bpo-46557: Log str(s) as msg instead of logger.warning("%s", s) + # since some log aggregation tools group logs by the msg arg + logger.warning(str(s)) + +def captureWarnings(capture): + """ + If capture is true, redirect all warnings to the logging package. + If capture is False, ensure that warnings are not redirected to logging + but to their original destinations. + """ + global _warnings_showwarning + if capture: + if _warnings_showwarning is None: + _warnings_showwarning = warnings.showwarning + warnings.showwarning = _showwarning + else: + if _warnings_showwarning is not None: + warnings.showwarning = _warnings_showwarning + _warnings_showwarning = None +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex/cli.html b/src/scitex/_sphinx_html/_modules/scitex/cli.html new file mode 100644 index 000000000..515227685 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex/cli.html @@ -0,0 +1,888 @@ + + + + + + + + scitex.cli — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex.cli

+#!/usr/bin/env python3
+"""
+SciTeX CLI - Command-line interface for SciTeX platform
+
+Provides unified interface for:
+- Cloud operations (wraps tea for Gitea)
+- Scholar operations (Django API)
+- Code operations (Django API)
+- Viz operations (Django API)
+- Writer operations (Django API)
+- Project operations (integrated workflows)
+"""
+
+import click
+
+from .main import cli
+
+
+
+
+
+
+
+[docs] +def format_python_signature(func, multiline: bool = True, indent: str = " ") -> tuple: + """Format Python function signature with colors matching mcp list-tools. + + Returns (name_colored, signature_colored) + """ + import inspect + + try: + sig = inspect.signature(func) + except (ValueError, TypeError): + return click.style(func.__name__, fg="green", bold=True), "" + + params = [] + for name, param in sig.parameters.items(): + # Get type annotation + if param.annotation != inspect.Parameter.empty: + ann = param.annotation + type_str = ann.__name__ if hasattr(ann, "__name__") else str(ann) + type_str = type_str.replace("typing.", "") + else: + type_str = None + + # Get default value + if param.default != inspect.Parameter.empty: + default = param.default + def_str = repr(default) if len(repr(default)) < 20 else "..." + if type_str: + p = f"{click.style(name, fg='white', bold=True)}: {click.style(type_str, fg='cyan')} = {click.style(def_str, fg='yellow')}" + else: + p = f"{click.style(name, fg='white', bold=True)} = {click.style(def_str, fg='yellow')}" + else: + if type_str: + p = f"{click.style(name, fg='white', bold=True)}: {click.style(type_str, fg='cyan')}" + else: + p = click.style(name, fg="white", bold=True) + params.append(p) + + # Return type + ret_str = "" + if sig.return_annotation != inspect.Parameter.empty: + ret = sig.return_annotation + ret_name = ret.__name__ if hasattr(ret, "__name__") else str(ret) + ret_name = ret_name.replace("typing.", "") + ret_str = f" -> {click.style(ret_name, fg='magenta')}" + + name_s = click.style(func.__name__, fg="green", bold=True) + + if multiline and len(params) > 2: + param_indent = indent + " " + params_str = ",\n".join(f"{param_indent}{p}" for p in params) + sig_s = f"(\n{params_str}\n{indent}){ret_str}" + else: + sig_s = f"({', '.join(params)}){ret_str}" + + return name_s, sig_s
+ + + +
+[docs] +def group_to_json(ctx, group: click.Group) -> None: + """Output a group's subcommands as Result JSON and exit. + + Provides ``--json`` behavior for group-level commands, listing + available subcommands with their short help text. + """ + from scitex_dev import Result + + cmds = {} + for name in sorted(group.list_commands(ctx) or []): + cmd = group.get_command(ctx, name) + if cmd: + cmds[name] = cmd.get_short_help_str(limit=150) + click.echo(Result(success=True, data={"commands": cmds}).to_json()) + ctx.exit(0)
+ + + +
+[docs] +def help_recursive_to_json(ctx, group: click.Group) -> None: + """Output recursive help for a group and all subcommands as Result JSON. + + Walks the entire command tree and outputs structured JSON with + command names, help text, options, and nested subcommands. + """ + from scitex_dev import Result + + def _cmd_to_dict(cmd, parent_ctx): + """Convert a Click command to a dict with options and help.""" + info = { + "help": cmd.get_short_help_str(limit=300), + } + # Collect options (exclude --help) + params = [] + for param in cmd.params: + if isinstance(param, click.Option) and param.name != "help": + p = { + "name": param.name, + "flags": list(param.opts), + "required": param.required, + "type": param.type.name + if hasattr(param.type, "name") + else str(param.type), + } + if param.default is not None: + p["default"] = param.default + if param.help: + p["help"] = param.help + params.append(p) + elif isinstance(param, click.Argument): + p = { + "name": param.name, + "type": "argument", + "required": param.required, + } + params.append(p) + if params: + info["params"] = params + + # Recurse into subgroups + if isinstance(cmd, click.MultiCommand): + subs = {} + for name in sorted(cmd.list_commands(parent_ctx) or []): + sub_cmd = cmd.get_command(parent_ctx, name) + if sub_cmd: + with click.Context( + sub_cmd, info_name=name, parent=parent_ctx + ) as sub_ctx: + subs[name] = _cmd_to_dict(sub_cmd, sub_ctx) + if subs: + info["subcommands"] = subs + return info + + # Build the full tree + group_name = group.name or "cli" + fake_parent = click.Context(click.Group(), info_name="scitex") + parent_ctx = click.Context(group, info_name=group_name, parent=fake_parent) + + tree = _cmd_to_dict(group, parent_ctx) + tree["name"] = f"scitex {group_name}" if group_name != "cli" else "scitex" + + click.echo(Result(success=True, data=tree).to_json()) + ctx.exit(0)
+ + + +__all__ = [ + "cli", + "print_help_recursive", + "format_python_signature", + "group_to_json", + "help_recursive_to_json", +] +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_app/_files_api.html b/src/scitex/_sphinx_html/_modules/scitex_app/_files_api.html new file mode 100644 index 000000000..b70a59fa3 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_app/_files_api.html @@ -0,0 +1,870 @@ + + + + + + + + scitex_app._files_api — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_app._files_api

+#!/usr/bin/env python3
+# Timestamp: 2026-05-12
+# File: scitex_app/_files_api.py
+
+"""Flat module-level wrappers around `FilesBackend` operations.
+
+These mirror the package's MCP tool surface (`app_read_file`,
+`app_write_file`, …) so users have a single-call Python API that
+matches what MCP exposes — closing the API↔MCP parity check
+(§6) and giving users a more discoverable surface for one-off
+file operations than `get_files(root).read(path)`.
+
+Each wrapper resolves the backend via :func:`scitex_app.sdk.get_files`
+on every call, so it honours the same auto-detection rules
+(``SCITEX_API_TOKEN`` for cloud, filesystem otherwise) and stays
+in lock-step with whatever backend the caller has registered.
+
+Heavy file shims (binary handling, traversal safety) live on the
+backend; this module is intentionally thin.
+"""
+
+from __future__ import annotations
+
+from pathlib import Path
+from typing import List, Optional, Union
+
+from .sdk import get_files
+
+
+
+[docs] +def read_file( + path: str, + *, + root: Union[str, Path] = ".", + binary: bool = False, +) -> Union[str, bytes]: + """Read a single file via the resolved :class:`FilesBackend`. + + Equivalent to ``get_files(root).read(path, binary=binary)``; + mirrors the ``app_read_file`` MCP tool. + """ + return get_files(root).read(path, binary=binary)
+ + + +
+[docs] +def write_file( + path: str, + content: Union[str, bytes], + *, + root: Union[str, Path] = ".", +) -> None: + """Write a file via the resolved :class:`FilesBackend`. + + Mirrors the ``app_write_file`` MCP tool. + """ + get_files(root).write(path, content)
+ + + +
+[docs] +def list_files( + directory: str = "", + *, + root: Union[str, Path] = ".", + extensions: Optional[List[str]] = None, +) -> List[str]: + """List file paths under *directory*. + + Mirrors the ``app_list_files`` MCP tool. + """ + return get_files(root).list(directory, extensions=extensions)
+ + + +
+[docs] +def file_exists(path: str, *, root: Union[str, Path] = ".") -> bool: + """Return whether *path* exists in the resolved backend. + + Mirrors the ``app_file_exists`` MCP tool. + """ + return get_files(root).exists(path)
+ + + +
+[docs] +def delete_file(path: str, *, root: Union[str, Path] = ".") -> None: + """Delete *path* in the resolved backend. + + Mirrors the ``app_delete_file`` MCP tool. + """ + get_files(root).delete(path)
+ + + +
+[docs] +def copy_file( + src_path: str, + dest_path: str, + *, + root: Union[str, Path] = ".", +) -> None: + """Copy *src_path* to *dest_path* within the resolved backend. + + Mirrors the ``app_copy_file`` MCP tool. + """ + get_files(root).copy(src_path, dest_path)
+ + + +
+[docs] +def rename_file( + old_path: str, + new_path: str, + *, + root: Union[str, Path] = ".", +) -> None: + """Rename *old_path* to *new_path* within the resolved backend. + + Mirrors the ``app_rename_file`` MCP tool. + """ + get_files(root).rename(old_path, new_path)
+ + + +
+[docs] +def scaffold( + target_dir: Union[str, Path] = ".", + *, + name: Optional[str] = None, + label: Optional[str] = None, + icon: str = "fas fa-puzzle-piece", + description: str = "", + frontend: str = "html", + overwrite: bool = False, +) -> List[Path]: + """Generate a new SciTeX workspace app skeleton. + + Mirrors the ``app_scaffold`` MCP tool. Auto-appends ``_app`` / + ``-app`` to *name* (matching the MCP tool's behaviour) so + Python and MCP callers see the same result for the same inputs. + """ + from .appmaker import init_app + + target = Path(target_dir).resolve() + app_name = name or target.name + + if not (app_name.endswith("_app") or app_name.endswith("-app")): + sep = "-" if "-" in app_name else "_" + app_name = f"{app_name}{sep}app" + + return init_app( + target_dir=target, + name=app_name, + label=label or "", + icon=icon, + description=description, + overwrite=overwrite, + frontend_type=frontend, + )
+ + + +
+[docs] +def validate(app_dir: Union[str, Path] = ".") -> List[str]: + """Audit a SciTeX app for cloud-submission readiness. + + Mirrors the ``app_validate`` MCP tool. Returns the list of + errors (empty when the app is ready). + """ + from .appmaker import validate as _validate + + return _validate(app_dir)
+ + + +__all__ = [ + "read_file", + "write_file", + "list_files", + "file_exists", + "delete_file", + "copy_file", + "rename_file", + "scaffold", + "validate", +] + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_app/sdk.html b/src/scitex/_sphinx_html/_modules/scitex_app/sdk.html new file mode 100644 index 000000000..70fa16bca --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_app/sdk.html @@ -0,0 +1,815 @@ + + + + + + + + scitex_app.sdk — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_app.sdk

+#!/usr/bin/env python3
+# Timestamp: 2026-03-13
+# File: scitex_app/sdk/__init__.py
+
+"""App SDK — write-once interface for local + cloud SciTeX apps.
+
+Usage (standalone / local):
+    from scitex_app.sdk import get_files
+
+    files = get_files("./my_project")
+    content = files.read("recipes/my_recipe.yaml")
+    files.write("output/result.png", png_bytes)
+
+Usage (cloud, auto-detected via SCITEX_API_TOKEN):
+    files = get_files()  # routes through cloud REST API
+
+Usage (remote local, via SCITEX_API_URL):
+    import os
+    os.environ["SCITEX_API_TOKEN"] = "your-token"
+    os.environ["SCITEX_API_URL"] = "https://scitex.ai"
+    files = get_files()  # routes to remote cloud
+"""
+
+from __future__ import annotations
+
+import os
+from pathlib import Path
+from typing import Any, Callable, Dict, Optional, Union
+
+from ._protocol import FilesBackend
+from ._tree import build_tree
+
+# Backend registry: name -> factory callable
+_registry: Dict[str, Callable[..., FilesBackend]] = {}
+
+
+
+[docs] +def register_backend(name: str, factory: Callable[..., FilesBackend]) -> None: + """Register a files backend factory. + + Parameters + ---------- + name : str + Backend identifier (e.g., "cloud", "s3"). + factory : callable + Callable(root, ``**kwargs``) -> ``FilesBackend`` instance. + """ + _registry[name] = factory
+ + + +
+[docs] +def get_files( + root: Optional[Union[str, Path]] = None, + *, + backend: Optional[str] = None, + **kwargs: Any, +) -> FilesBackend: + """Get a files backend instance. + + Auto-detection logic: + + 1. If ``backend`` is specified, use that. + 2. If ``SCITEX_API_TOKEN`` env var is set and "cloud" backend + is registered, use cloud. + 3. Otherwise, use filesystem (default). + + Parameters + ---------- + root : str or Path, optional + Root directory for filesystem backend. Defaults to cwd. + backend : str, optional + Explicit backend name. If None, auto-detected. + + Returns + ------- + FilesBackend + A backend instance. + + Raises + ------ + KeyError + If the requested backend is not registered. + """ + if backend: + if backend not in _registry: + raise KeyError( + f"Backend {backend!r} not registered. " + f"Available: {list(_registry.keys())}" + ) + return _registry[backend](root, **kwargs) + + if os.environ.get("SCITEX_API_TOKEN"): + if "cloud" not in _registry: + # Auto-register cloud backend when token is available + from ._cloud_files import cloud_files_factory + + _registry["cloud"] = cloud_files_factory + return _registry["cloud"](root, **kwargs) + + from ._filesystem import FileSystemBackend + + return FileSystemBackend(root or Path.cwd())
+ + + +__all__ = [ + "FilesBackend", + "get_files", + "register_backend", + "build_tree", +] + + +# Internal cloud modules — accessible via scitex_app.sdk._client etc. +# but NOT part of the public API contract. Use scitex_cloud.sdk for +# cloud service access (data, files, jobs, scitex, external). +def __getattr__(name: str) -> Any: + """Lazy-load cloud internals on demand (not in __all__).""" + _lazy = { + "PlatformClient": ("._client", "PlatformClient"), + "get_client": ("._client", "get_client"), + "reset_client": ("._client", "reset_client"), + "CloudFilesBackend": ("._cloud_files", "CloudFilesBackend"), + "data": (".", "_cloud_data"), + "files": (".", "_cloud_files"), + "jobs": (".", "_cloud_jobs"), + "scitex": (".", "_cloud_scitex"), + "external": (".", "_cloud_external"), + } + if name in _lazy: + import importlib + + mod_path, attr = _lazy[name] + if mod_path == ".": + return importlib.import_module(f".{attr}", __package__) + mod = importlib.import_module(mod_path, __package__) + return getattr(mod, attr) + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_app/sdk/_protocol.html b/src/scitex/_sphinx_html/_modules/scitex_app/sdk/_protocol.html new file mode 100644 index 000000000..be64f6949 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_app/sdk/_protocol.html @@ -0,0 +1,810 @@ + + + + + + + + scitex_app.sdk._protocol — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_app.sdk._protocol

+#!/usr/bin/env python3
+# Timestamp: 2026-03-13
+# File: scitex_app/sdk/_protocol.py
+
+"""FilesBackend protocol — structural typing for pluggable storage."""
+
+from __future__ import annotations
+
+from typing import List, Optional, Protocol, Union, runtime_checkable
+
+
+
+[docs] +@runtime_checkable +class FilesBackend(Protocol): + """File storage backend protocol. + + Implementations must provide these 7 methods. + Uses ``typing.Protocol`` for structural subtyping — backends + just implement the methods, no inheritance required. + + Implementations + --------------- + - ``FileSystemBackend`` — local pathlib (ships with scitex-app) + - ``CloudFilesBackend`` — HTTP via scitex_cloud (provided at runtime) + """ + +
+[docs] + def read(self, path: str, *, binary: bool = False) -> Union[str, bytes]: + """Read file content. + + Parameters + ---------- + path : str + Relative path within the backend's namespace. + binary : bool + If True, return bytes; otherwise return str. + + Raises + ------ + FileNotFoundError + If the file does not exist. + """ + ...
+ + +
+[docs] + def write(self, path: str, content: Union[str, bytes]) -> None: + """Write content to a file, creating parent dirs as needed. + + Parameters + ---------- + path : str + Relative path within the backend's namespace. + content : str or bytes + Text or binary content. + """ + ...
+ + +
+[docs] + def list( + self, + directory: str = "", + *, + extensions: Optional[List[str]] = None, + ) -> List[str]: + """List file paths in a directory. + + Parameters + ---------- + directory : str + Relative directory path ("" = root). + extensions : list of str, optional + Filter by extension, e.g. [".yaml", ".png"]. + + Returns + ------- + list of str + Relative file paths. + """ + ...
+ + +
+[docs] + def exists(self, path: str) -> bool: + """Check if a file exists.""" + ...
+ + +
+[docs] + def delete(self, path: str) -> None: + """Delete a file. + + Raises + ------ + FileNotFoundError + If the file does not exist. + """ + ...
+ + +
+[docs] + def rename(self, old_path: str, new_path: str) -> None: + """Rename/move a file within the namespace. + + Raises + ------ + FileNotFoundError + If old_path does not exist. + FileExistsError + If new_path already exists. + """ + ...
+ + +
+[docs] + def copy(self, src_path: str, dest_path: str) -> None: + """Copy a file within the namespace. + + Raises + ------ + FileNotFoundError + If src_path does not exist. + """ + ...
+
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_app/sdk/_tree.html b/src/scitex/_sphinx_html/_modules/scitex_app/sdk/_tree.html new file mode 100644 index 000000000..5a4a76354 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_app/sdk/_tree.html @@ -0,0 +1,821 @@ + + + + + + + + scitex_app.sdk._tree — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_app.sdk._tree

+#!/usr/bin/env python3
+# Timestamp: 2026-03-15
+# File: scitex_app/sdk/_tree.py
+
+"""Tree builder utility for FilesBackend.
+
+Converts flat file listings into nested directory tree structures.
+Reusable by any app that needs a file browser UI.
+"""
+
+from __future__ import annotations
+
+from pathlib import Path
+from typing import Any, Dict, List, Optional
+
+
+
+[docs] +def build_tree( + backend: Any, + directory: str = "", + *, + extensions: Optional[List[str]] = None, + skip_hidden: bool = True, + max_depth: int = 10, +) -> List[Dict[str, Any]]: + """Build a nested tree structure from a FilesBackend. + + Parameters + ---------- + backend : FilesBackend + A file storage backend implementing the FilesBackend protocol. + directory : str + Starting directory (relative to backend root). Default: root. + extensions : list of str, optional + Filter files by extension (e.g., [".yaml", ".png"]). + Directories are always included for traversal. + skip_hidden : bool + Skip files/directories starting with ".". Default: True. + max_depth : int + Maximum recursion depth to prevent runaway traversal. Default: 10. + + Returns + ------- + list of dict + Nested tree structure:: + + [ + {"path": "subdir", "name": "subdir", "type": "directory", + "children": [...]}, + {"path": "file.yaml", "name": "file.yaml", "type": "file"}, + ] + """ + if max_depth <= 0: + return [] + + # Get all entries in this directory + entries = _list_entries(backend, directory) + items = [] + + for entry in entries: + name = Path(entry["path"]).name + + if skip_hidden and name.startswith("."): + continue + + if entry["type"] == "directory": + try: + children = build_tree( + backend, + entry["path"], + extensions=extensions, + skip_hidden=skip_hidden, + max_depth=max_depth - 1, + ) + except PermissionError: + continue # skip directories we can't read + if children: # only include non-empty directories + items.append( + { + "path": entry["path"], + "name": name, + "type": "directory", + "children": children, + } + ) + else: + # Apply extension filter to files + if extensions: + suffix = Path(name).suffix.lower() + if suffix not in extensions: + continue + items.append( + { + "path": entry["path"], + "name": name, + "type": "file", + } + ) + + # Sort: pure alphabetical (case-insensitive), dirs and files interleaved + items.sort(key=lambda x: x["name"].lower()) + return items
+ + + +def _list_entries( + backend: Any, + directory: str = "", +) -> List[Dict[str, str]]: + """List directory entries with type information. + + Tries backend.list_entries() first (if available, returns files + dirs). + Falls back to backend.list() for files and attempts directory discovery. + """ + # Preferred: backend supports list_entries() returning typed entries + if hasattr(backend, "list_entries"): + return backend.list_entries(directory) + + # Fallback: use list() for files, try to discover directories + # via the backend's internal structure + if hasattr(backend, "_root"): + # FileSystemBackend — access pathlib directly for directory info + root = backend._root + target = (root / directory) if directory else root + if not target.is_dir(): + return [] + entries = [] + try: + children = sorted(target.iterdir(), key=lambda x: x.name.lower()) + except PermissionError: + return [] + for item in children: + try: + rel = str(item.relative_to(root)) + if item.is_dir(): + entries.append({"path": rel, "type": "directory"}) + elif item.is_file(): + entries.append({"path": rel, "type": "file"}) + except PermissionError: + continue # skip files/dirs we can't access + return entries + + # Last resort: list() returns only files, no directory info + files = backend.list(directory) + return [{"path": f, "type": "file"} for f in files] + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_audit/_runner.html b/src/scitex/_sphinx_html/_modules/scitex_audit/_runner.html new file mode 100644 index 000000000..f84add944 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_audit/_runner.html @@ -0,0 +1,801 @@ + + + + + + + + scitex_audit._runner — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_audit._runner

+#!/usr/bin/env python3
+# File: src/scitex_audit/_runner.py
+
+"""
+Core audit orchestrator.
+
+Discovers available tools and runs each requested checker,
+returning a unified results dictionary.
+"""
+
+from __future__ import annotations
+
+import logging
+import shutil
+from pathlib import Path
+from typing import Optional
+
+from ._bandit import run_bandit
+from ._format import format_json
+from ._github import run_github_check
+from ._pip_audit import run_pip_audit
+from ._shellcheck import run_shellcheck
+
+logger = logging.getLogger(__name__)
+
+ALL_CHECKS = ("python", "shell", "deps", "github")
+
+_TOOL_REQUIREMENTS = {
+    "python": "bandit",
+    "shell": "shellcheck",
+    "deps": "pip-audit",
+}
+
+
+def _is_tool_available(tool_name: str) -> bool:
+    """Check whether a CLI tool is on PATH."""
+    return shutil.which(tool_name) is not None
+
+
+def _is_gh_authenticated() -> bool:
+    """Check whether gh CLI is installed and authenticated."""
+    if not _is_tool_available("gh"):
+        return False
+    try:
+        import subprocess
+        result = subprocess.run(
+            ["gh", "auth", "status"],
+            capture_output=True,
+            text=True,
+            timeout=10,
+        )
+        return result.returncode == 0
+    except Exception:
+        return False
+
+
+def _skipped_result(tool_name: str) -> dict:
+    """Return a standard skipped result."""
+    return {
+        "status": "skipped",
+        "findings": [],
+        "summary": f"Skipped ({tool_name} not installed)",
+    }
+
+
+
+[docs] +def audit( + path: str = ".", + checks: Optional[list[str]] = None, + output_file: Optional[str] = None, +) -> dict: + """Run security audit across multiple tools. + + Parameters + ---------- + path : str + Directory to scan. Defaults to current directory. + checks : list[str] | None + Which checks to run. Options: "python", "shell", "deps", "github". + None means run all available checks. + output_file : str | None + If given, write JSON report to this path. + + Returns + ------- + dict + Keys are check names, values have {status, findings, summary}. + """ + target = Path(path).resolve() + requested = list(checks) if checks else list(ALL_CHECKS) + results: dict = {} + + for check in requested: + if check not in ALL_CHECKS: + logger.warning("Unknown check %r, skipping", check) + continue + + if check == "github": + if not _is_gh_authenticated(): + logger.info("GitHub CLI not available or not authenticated, skipping") + results["github"] = _skipped_result("gh") + continue + results["github"] = run_github_check() + + else: + tool_name = _TOOL_REQUIREMENTS[check] + if not _is_tool_available(tool_name): + logger.info("%s not installed, skipping %s check", tool_name, check) + results[check] = _skipped_result(tool_name) + continue + + if check == "python": + results["python"] = run_bandit(target) + elif check == "shell": + results["shell"] = run_shellcheck(target) + elif check == "deps": + results["deps"] = run_pip_audit(target) + + if output_file: + out_path = Path(output_file) + out_path.parent.mkdir(parents=True, exist_ok=True) + out_path.write_text(format_json(results)) + logger.info("Report written to %s", out_path) + + return results
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_clew.html b/src/scitex/_sphinx_html/_modules/scitex_clew.html new file mode 100644 index 000000000..2f10f14a8 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_clew.html @@ -0,0 +1,1112 @@ + + + + + + + + scitex_clew — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_clew

+#!/usr/bin/env python3
+"""
+scitex-clew — Hash-based verification for reproducible science.
+
+Standalone package. Zero dependencies (pure stdlib + sqlite3).
+When used with scitex, integration is automatic via @stx.session + stx.io.
+
+Public API::
+
+    import scitex_clew as clew
+
+    # Verification
+    clew.status()                      # git-status-like overview
+    clew.run(session_id)               # verify one run (hash check)
+    clew.chain(target_file)            # trace file → source chain
+    clew.dag(targets)                  # verify full DAG
+    clew.rerun(target)                 # re-execute & compare (sandbox)
+    clew.rerun_dag(targets)            # rerun full DAG in topo order
+    clew.rerun_claims()                # rerun all claim-backing sessions
+    clew.list_runs(limit=100)          # list tracked runs
+    clew.stats()                       # database statistics
+
+    # Claims
+    clew.add_claim(...)                # register manuscript assertion
+    clew.list_claims(...)              # list registered claims
+    clew.verify_claim(...)             # verify a specific claim
+
+    # Stamping
+    clew.stamp(...)                    # create temporal proof
+    clew.list_stamps(...)              # list stamps
+    clew.check_stamp(...)              # verify a stamp
+
+    # Hashing
+    clew.hash_file(path)               # SHA256 of a file
+    clew.hash_directory(path)          # SHA256 of all files in dir
+
+    # Visualization
+    clew.mermaid(...)                  # generate Mermaid DAG diagram
+
+    # Examples
+    clew.init_examples(dest)           # scaffold example pipeline
+
+    # Session lifecycle hooks (invoked by @scitex.session)
+    clew.on_session_start(session_id)  # open a tracked run
+    clew.on_session_close(status=...)  # finalize run + combined hash
+"""
+
+from __future__ import annotations
+
+try:
+    from importlib.metadata import version as _v, PackageNotFoundError
+
+    try:
+        __version__ = _v("scitex-clew")
+    except PackageNotFoundError:
+        __version__ = "0.0.0+local"
+    del _v, PackageNotFoundError
+except ImportError:  # pragma: no cover — only on ancient Pythons
+    __version__ = "0.0.0+local"
+
+# ---------------------------------------------------------------------------
+# Optional decorator from scitex-dev (graceful fallback)
+# ---------------------------------------------------------------------------
+try:
+    from scitex_dev.decorators import supports_return_as as _supports_return_as
+except Exception:
+    # Broad catch (not just ImportError): scitex-dev may import optional ML
+    # libs whose runtime-init can fail with VersionError / RuntimeError.
+    # Fall back to a no-op decorator regardless.
+    def _supports_return_as(fn):
+        return fn
+
+
+# ---------------------------------------------------------------------------
+# Internal imports (hidden from public API, still importable via full path)
+# ---------------------------------------------------------------------------
+from . import groupers  # public: scitex_clew.groupers
+from ._chain import (
+    ChainVerification as _ChainVerification,
+)
+from ._chain import (
+    DAGVerification as _DAGVerification,
+)
+from ._chain import (
+    FileVerification as _FileVerification,
+)
+from ._chain import (
+    RunVerification as _RunVerification,
+)
+from ._chain import (
+    VerificationLevel as _VerificationLevel,
+)
+from ._chain import (
+    VerificationStatus as _VerificationStatus,
+)
+from ._chain import (
+    get_status as _get_status,
+)
+from ._chain import (
+    verify_chain as _verify_chain,
+)
+from ._chain import (
+    verify_file as _verify_file,
+)
+from ._chain import (
+    verify_run as _verify_run,
+)
+from ._claim import (
+    Claim as _Claim,
+)
+from ._claim import (
+    add_claim,
+    export_claims_json,
+    list_claims,
+    verify_claim,
+)
+from ._register_intermediate import register_intermediate
+from ._observers import on_session_close, on_session_start
+from ._claim import (
+    format_claims as _format_claims,
+)
+from ._claim import (
+    verify_claims_dag as _verify_claims_dag,
+)
+from ._dag import verify_dag as _verify_dag
+from ._dag import verify_dag_strict as _verify_dag_strict
+from ._db import VerificationDB as _VerificationDB
+from ._db import get_db as _get_db
+from ._db import set_db as _set_db
+from ._examples import init_examples
+from ._hash import (
+    combine_hashes as _combine_hashes,
+)
+from ._hash import (
+    hash_directory,
+    hash_file,
+)
+from ._hash import (
+    hash_files as _hash_files,
+)
+from ._hash import (
+    verify_hash as _verify_hash,
+)
+from ._registry import ClewRegistry as _ClewRegistry
+from ._registry import get_registry as _get_registry
+from ._rerun import rerun_claims, rerun_dag
+from ._rerun import verify_by_rerun as _verify_by_rerun
+from ._stamp import Stamp as _Stamp
+from ._stamp import check_stamp, list_stamps, stamp
+from ._tracker import (
+    SessionTracker as _SessionTracker,
+)
+from ._tracker import (
+    get_tracker as _get_tracker,
+)
+from ._tracker import (
+    set_tracker as _set_tracker,
+)
+from ._tracker import (
+    start_tracking as _start_tracking,
+)
+from ._tracker import (
+    stop_tracking as _stop_tracking,
+)
+from ._visualize import (
+    format_chain_verification as _format_chain_verification,
+)
+from ._visualize import (
+    format_list as _format_list,
+)
+from ._visualize import (
+    format_run_detailed as _format_run_detailed,
+)
+from ._visualize import (
+    format_run_verification as _format_run_verification,
+)
+from ._visualize import (
+    format_status as _format_status,
+)
+from ._visualize import (
+    generate_html_dag as _generate_html_dag,
+)
+from ._visualize import (
+    generate_mermaid_dag as _generate_mermaid_dag,
+)
+from ._visualize import (
+    print_verification_summary as _print_verification_summary,
+)
+from ._visualize import (
+    render_dag as _render_dag,
+)
+
+
+# ---------------------------------------------------------------------------
+# Public convenience API
+# ---------------------------------------------------------------------------
+
+[docs] +@_supports_return_as +def list_runs(limit: int = 100, status: str = None): + """List tracked runs.""" + db = _get_db() + return db.list_runs(status=status, limit=limit)
+ + + +
+[docs] +@_supports_return_as +def status(): + """Get verification status summary (like git status).""" + return _get_status()
+ + + +
+[docs] +@_supports_return_as +def run(session_id: str, from_scratch: bool = False): + """Verify a specific run. + + Parameters + ---------- + session_id : str + Session identifier + from_scratch : bool, optional + If True, re-execute the script and verify outputs (slow but thorough). + If False, only compare hashes (fast). + """ + if from_scratch: + return _verify_by_rerun(session_id) + return _verify_run(session_id)
+ + + +
+[docs] +@_supports_return_as +def chain(target: str): + """Verify the dependency chain for a target file.""" + return _verify_chain(target)
+ + + +
+[docs] +@_supports_return_as +def stats(): + """Get database statistics.""" + db = _get_db() + return db.stats()
+ + + +
+[docs] +@_supports_return_as +def dag(targets=None, claims=False, strict=False): + """Verify the DAG for multiple targets or all claims. + + Parameters + ---------- + targets : list of str or Path, optional + Target files to verify (mutually exclusive with ``claims``). + claims : bool, optional + If True, build the DAG from every registered claim. + strict : bool, optional + If True (F2), return a failure-attribution dict with + ``failed_node`` / ``root_cause`` / ``invalidated_claims`` / + ``still_valid_claims`` instead of a ``DAGVerification``. + """ + if strict: + return _verify_dag_strict(targets=targets, claims=claims) + if claims: + return _verify_claims_dag() + return _verify_dag(targets or [])
+ + + +
+[docs] +@_supports_return_as +def rerun(target, timeout: int = 300, cleanup: bool = True): + """Re-execute a session in a sandbox and compare outputs. + + Parameters + ---------- + target : str or list[str] + Session ID, script path, or artifact path. + timeout : int, optional + Maximum execution time in seconds (default: 300). + cleanup : bool, optional + Remove sandbox outputs after verification (default: True). + """ + return _verify_by_rerun(target, timeout=timeout, cleanup=cleanup)
+ + + +
+[docs] +@_supports_return_as +def mermaid( + session_id=None, + target_file=None, + target_files=None, + claims=False, + grouper=None, + **kwargs, +): + """Generate a Mermaid DAG diagram. + + Parameters + ---------- + session_id : str, optional + Start from this session. + target_file : str, optional + Start from the session that produced this file. + target_files : list of str, optional + Multiple target files (multi-target DAG). + claims : bool, optional + If True, build DAG from all registered claims. + grouper : callable | dict | None, optional + File grouping strategy. Callable or JSON/dict spec (see + ``scitex_clew.groupers.resolve_spec``). If ``None``, falls back to + ``.scitex/clew/config.yaml`` (key ``grouper``) if present. + """ + if grouper is None: + from ._groupers._config import load_project_config + + grouper = load_project_config().get("grouper") + return _generate_mermaid_dag( + session_id=session_id, + target_file=target_file, + target_files=target_files, + claims=claims, + grouper=grouper, + **kwargs, + )
+ + + +# --------------------------------------------------------------------------- +# Accessible but not in __all__ (for advanced use / backward compat) +# --------------------------------------------------------------------------- +get_db = _get_db +set_db = _set_db +verify_run = _verify_run +verify_chain = _verify_chain +verify_dag = _verify_dag +verify_file = _verify_file +verify_by_rerun = _verify_by_rerun +verify_claims_dag = _verify_claims_dag +get_status = _get_status +generate_mermaid_dag = _generate_mermaid_dag +get_tracker = _get_tracker +set_tracker = _set_tracker +start_tracking = _start_tracking +stop_tracking = _stop_tracking +get_registry = _get_registry +format_claims = _format_claims +format_status = _format_status +format_list = _format_list +format_run_verification = _format_run_verification +format_run_detailed = _format_run_detailed +format_chain_verification = _format_chain_verification +print_verification_summary = _print_verification_summary +generate_html_dag = _generate_html_dag +render_dag = _render_dag +combine_hashes = _combine_hashes +hash_files = _hash_files +verify_hash = _verify_hash +verify_run_from_scratch = _verify_by_rerun + +# Class/type names +VerificationDB = _VerificationDB +SessionTracker = _SessionTracker +ClewRegistry = _ClewRegistry +VerificationStatus = _VerificationStatus +VerificationLevel = _VerificationLevel +FileVerification = _FileVerification +RunVerification = _RunVerification +ChainVerification = _ChainVerification +DAGVerification = _DAGVerification +Claim = _Claim +Stamp = _Stamp + + +# --------------------------------------------------------------------------- +# Public API — only these 19 names show in dir() and tab-completion +# --------------------------------------------------------------------------- +__all__ = [ + "__version__", + # Verification + "status", + "run", + "chain", + "dag", + "rerun", + "rerun_dag", + "rerun_claims", + "list_runs", + "stats", + # Claims + "add_claim", + "list_claims", + "verify_claim", + "export_claims_json", + "register_intermediate", + # Stamping + "stamp", + "list_stamps", + "check_stamp", + # Hashing + "hash_file", + "hash_directory", + # Visualization + "mermaid", + # Grouping API + "groupers", + # Examples + "init_examples", + # Session lifecycle hooks + "on_session_start", + "on_session_close", +] + + +# --------------------------------------------------------------------------- +# SOC R6: self-register post-save / post-load hooks with scitex-io. +# Must never break ``import scitex_clew`` — broad except is intentional. +# --------------------------------------------------------------------------- +try: + from ._observers import register_with_scitex_io as _register + + _register() + del _register +except Exception: + pass + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_clew/_claim.html b/src/scitex/_sphinx_html/_modules/scitex_clew/_claim.html new file mode 100644 index 000000000..bc7c55224 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_clew/_claim.html @@ -0,0 +1,1308 @@ + + + + + + + + scitex_clew._claim — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_clew._claim

+#!/usr/bin/env python3
+# Timestamp: "2026-02-09 (ywatanabe)"
+# File: /home/ywatanabe/proj/scitex-python/src/scitex/verify/_claim.py
+"""Claim layer — link paper assertions to verification chain.
+
+Claims represent specific assertions in manuscripts (statistics, figures,
+tables) that can be traced back through the verification chain to source data.
+
+Five claim types:
+  - statistic: A numerical result (p-value, effect size, etc.)
+  - figure:    A figure reference linked to a recipe/image
+  - table:     A table reference linked to source CSV
+  - text:      A textual assertion linked to computational output
+  - value:     A specific computed value (count, percentage, etc.)
+"""
+
+from __future__ import annotations
+
+import json
+import os
+import re
+import sqlite3
+from dataclasses import dataclass
+from datetime import datetime
+from pathlib import Path
+from typing import Dict, List, Optional, Union
+
+from ._db import get_db
+
+# Canonical claim types
+CLAIM_TYPES = ("statistic", "figure", "table", "text", "value")
+
+
+@dataclass
+class Claim:
+    """A traceable assertion in a manuscript."""
+
+    claim_id: str
+    file_path: str
+    line_number: Optional[int]
+    claim_type: str
+    claim_value: Optional[str]
+    source_session: Optional[str]
+    source_file: Optional[str]
+    source_hash: Optional[str]
+    registered_at: Optional[str] = None
+    verified_at: Optional[str] = None
+    status: str = "registered"
+
+    @property
+    def location(self) -> str:
+        """Human-readable location string."""
+        if self.line_number:
+            return f"{self.file_path}:L{self.line_number}"
+        return self.file_path
+
+    def to_dict(self) -> Dict:
+        return {
+            "claim_id": self.claim_id,
+            "file_path": self.file_path,
+            "line_number": self.line_number,
+            "claim_type": self.claim_type,
+            "claim_value": self.claim_value,
+            "source_session": self.source_session,
+            "source_file": self.source_file,
+            "source_hash": self.source_hash,
+            "registered_at": self.registered_at,
+            "verified_at": self.verified_at,
+            "status": self.status,
+        }
+
+
+def migrate_add_claims_table(db_path: Path) -> None:
+    """Create claims table if not present. Safe to call multiple times."""
+    conn = sqlite3.connect(str(db_path))
+    try:
+        conn.execute(
+            """
+            CREATE TABLE IF NOT EXISTS claims (
+                id INTEGER PRIMARY KEY AUTOINCREMENT,
+                claim_id TEXT UNIQUE NOT NULL,
+                file_path TEXT NOT NULL,
+                line_number INTEGER,
+                claim_type TEXT NOT NULL,
+                claim_value TEXT,
+                source_session TEXT,
+                source_file TEXT,
+                source_hash TEXT,
+                registered_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+                verified_at TIMESTAMP,
+                status TEXT DEFAULT 'registered'
+            )
+            """
+        )
+        conn.execute("CREATE INDEX IF NOT EXISTS idx_claims_file ON claims(file_path)")
+        conn.execute(
+            "CREATE INDEX IF NOT EXISTS idx_claims_source ON claims(source_file)"
+        )
+        conn.execute(
+            "CREATE INDEX IF NOT EXISTS idx_claims_session ON claims(source_session)"
+        )
+        conn.commit()
+    finally:
+        conn.close()
+
+
+def _generate_claim_id(
+    file_path: str, line_number: Optional[int], claim_type: str
+) -> str:
+    """Generate a deterministic claim ID."""
+    loc = f"{file_path}:L{line_number}" if line_number else file_path
+    import hashlib
+
+    h = hashlib.sha256(f"{loc}:{claim_type}".encode()).hexdigest()[:12]
+    return f"claim_{h}"
+
+
+
+[docs] +def add_claim( + file_path: str, + claim_type: str, + line_number: Optional[int] = None, + claim_value: Optional[str] = None, + source_file: Optional[str] = None, + source_session: Optional[str] = None, +) -> Claim: + """Register a claim linking a manuscript assertion to the verification chain. + + Parameters + ---------- + file_path : str + Path to the manuscript file (e.g., paper.tex). + claim_type : str + One of: statistic, figure, table, text, value. + line_number : int, optional + Line number in the manuscript. + claim_value : str, optional + The asserted value (e.g., "p = 0.003"). + source_file : str, optional + Path to the source file that produced this claim. + source_session : str, optional + Session ID that produced the source. + + Returns + ------- + Claim + The registered claim object. + """ + if claim_type not in CLAIM_TYPES: + raise ValueError( + f"Invalid claim_type '{claim_type}'. Must be one of: {CLAIM_TYPES}" + ) + + file_path = str(Path(file_path).resolve()) + claim_id = _generate_claim_id(file_path, line_number, claim_type) + + # Compute source hash if source_file exists + source_hash = None + if source_file: + source_file = str(Path(source_file).resolve()) + source_path = Path(source_file) + if source_path.exists(): + from ._hash import hash_file + + source_hash = hash_file(source_path) + + # Auto-detect source session if not provided + if source_file and not source_session: + db = get_db() + sessions = db.find_session_by_file(source_file, role="output") + if sessions: + source_session = sessions[0] + + claim = Claim( + claim_id=claim_id, + file_path=file_path, + line_number=line_number, + claim_type=claim_type, + claim_value=claim_value, + source_session=source_session, + source_file=source_file, + source_hash=source_hash, + ) + + # Store in database + db = get_db() + _ensure_claims_table(db) + conn = sqlite3.connect(str(db.db_path)) + try: + conn.execute( + """ + INSERT OR REPLACE INTO claims + (claim_id, file_path, line_number, claim_type, claim_value, + source_session, source_file, source_hash, status) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'registered') + """, + ( + claim.claim_id, + claim.file_path, + claim.line_number, + claim.claim_type, + claim.claim_value, + claim.source_session, + claim.source_file, + claim.source_hash, + ), + ) + conn.commit() + finally: + conn.close() + + # Auto-export the canonical claims.json so consumers (verifier, + # scitex-writer, human eyes) can read a stable artifact without + # talking to sqlite. Default ON; opt out with + # SCITEX_CLEW_AUTO_EXPORT_CLAIMS=0 if you're streaming thousands of + # claims and the per-call rewrite cost matters. The cost is O(N×K) + # where N is total claims in the DB and K is rewrite size — for + # typical research papers (N < 100, K < 50 KB) it's negligible. + if os.environ.get("SCITEX_CLEW_AUTO_EXPORT_CLAIMS", "1") != "0": + try: + export_claims_json() + except Exception as exc: # noqa: BLE001 + # Auto-export is a convenience layer — must not break the + # add_claim primary path if e.g. the runtime/ dir is + # read-only on this host. Log and continue. The user can + # call export_claims_json() explicitly to surface failures. + import warnings as _w + + _w.warn( + f"scitex_clew auto-export of claims.json failed " + f"(set SCITEX_CLEW_AUTO_EXPORT_CLAIMS=0 to silence): " + f"{exc!r}", + RuntimeWarning, + stacklevel=2, + ) + + return claim
+ + + +
+[docs] +def export_claims_json( + path: Optional[Union[str, Path]] = None, + *, + file_path_filter: Optional[str] = None, + read_only: bool = True, +) -> Path: + """Export every registered claim to a canonical JSON artifact. + + The exported file is the single human-readable + machine-consumable + view of the claims table in ``db.sqlite``. The DB remains the + source of truth; this JSON is a regenerable artifact. + + Path resolution (mirrors :func:`scitex_clew._db._core._default_db_path`):: + + 1. Explicit ``path`` argument. + 2. ``$SCITEX_CLEW_CLAIMS_JSON`` env var (escape hatch). + 3. ``<project_root>/.scitex/clew/runtime/claims.json`` + (project root = nearest ancestor dir with ``.git`` or + ``pyproject.toml``; falls back to cwd if none found). + + Parameters + ---------- + path : str | Path, optional + Override the resolved path. Useful for tests / one-off dumps. + file_path_filter : str, optional + When set, only claims registered against this manuscript file + path are exported. Default: every claim in the DB. + read_only : bool, optional + After writing, ``chmod 0o444`` the file so accidental edits + fail loudly at the OS layer. Default True (the file IS + derived). Set False for tests that need to mutate the file. + + Returns + ------- + Path + The path the artifact was written to (absolute). + + Examples + -------- + >>> import scitex_clew as clew + >>> clew.add_claim("paper.tex", "value", 42, "0.94", source_file="r.csv") + >>> # claims.json now auto-exported under ./.scitex/clew/runtime/ + >>> clew.export_claims_json() # idempotent — re-emit on demand + PosixPath('.../.scitex/clew/runtime/claims.json') + """ + from ._db import _core as _db_core + + if path is None: + env_path = os.environ.get("SCITEX_CLEW_CLAIMS_JSON") + if env_path: + path = Path(env_path) + else: + path = _db_core._default_claims_json_path( + _db_core._find_project_root() + ) + path = Path(path).resolve() + path.parent.mkdir(parents=True, exist_ok=True) + + claims = list_claims(file_path=file_path_filter, limit=10_000) + payload = { + "_note": ( + "AUTO-GENERATED by scitex_clew.export_claims_json() from " + "db.sqlite. Do NOT edit by hand — re-emit by calling " + "scitex_clew.export_claims_json() (default-on after every " + "clew.add_claim()) or by re-running your pipeline." + ), + "claims_count": len(claims), + "claims": [c.to_dict() for c in claims], + } + + # Clear any pre-existing read-only bit before rewriting. + if path.exists(): + try: + path.chmod(0o644) + except OSError: + pass + + path.write_text(json.dumps(payload, indent=2, default=str)) + + if read_only: + try: + path.chmod(0o444) + except OSError: + # Best-effort — on filesystems that don't support unix + # perms (e.g. some Windows mounts) this is a no-op. + pass + + return path
+ + + +
+[docs] +def list_claims( + file_path: Optional[str] = None, + claim_type: Optional[str] = None, + status: Optional[str] = None, + limit: int = 100, +) -> List[Claim]: + """List registered claims with optional filters. + + Parameters + ---------- + file_path : str, optional + Filter by manuscript file path. + claim_type : str, optional + Filter by claim type. + status : str, optional + Filter by verification status. + limit : int + Maximum number of claims to return. + + Returns + ------- + list of Claim + """ + db = get_db() + _ensure_claims_table(db) + + query = "SELECT * FROM claims WHERE 1=1" + params = [] + + if file_path: + file_path = str(Path(file_path).resolve()) + query += " AND file_path = ?" + params.append(file_path) + if claim_type: + query += " AND claim_type = ?" + params.append(claim_type) + if status: + query += " AND status = ?" + params.append(status) + + query += " ORDER BY file_path, line_number LIMIT ?" + params.append(limit) + + conn = sqlite3.connect(str(db.db_path)) + conn.row_factory = sqlite3.Row + try: + rows = conn.execute(query, params).fetchall() + return [ + Claim( + claim_id=row["claim_id"], + file_path=row["file_path"], + line_number=row["line_number"], + claim_type=row["claim_type"], + claim_value=row["claim_value"], + source_session=row["source_session"], + source_file=row["source_file"], + source_hash=row["source_hash"], + registered_at=row["registered_at"], + verified_at=row["verified_at"], + status=row["status"], + ) + for row in rows + ] + finally: + conn.close()
+ + + +
+[docs] +def verify_claim(claim_id_or_location: str) -> Dict: + """Verify a specific claim by checking its source against the verification chain. + + Parameters + ---------- + claim_id_or_location : str + Either a claim_id or a location string like "paper.tex:L42". + + Returns + ------- + dict + Verification result with claim details and chain status. + """ + db = get_db() + _ensure_claims_table(db) + + claim = _resolve_claim(claim_id_or_location, db) + if not claim: + return { + "status": "not_found", + "message": f"No claim found for '{claim_id_or_location}'", + } + + result = { + "claim": claim.to_dict(), + "source_verified": False, + "chain_verified": False, + "details": [], + } + + # Check source file exists and hash matches + if claim.source_file: + source_path = Path(claim.source_file) + if not source_path.exists(): + result["details"].append(f"Source file missing: {claim.source_file}") + _update_claim_status(claim.claim_id, "missing", db) + result["claim"]["status"] = "missing" + return result + + from ._hash import hash_file + + current_hash = hash_file(source_path) + if ( + claim.source_hash + and current_hash[: len(claim.source_hash)] + == claim.source_hash[: len(current_hash)] + ): + result["source_verified"] = True + result["details"].append("Source file hash matches") + else: + result["details"].append( + f"Source hash mismatch: stored={claim.source_hash}, current={current_hash}" + ) + _update_claim_status(claim.claim_id, "mismatch", db) + result["claim"]["status"] = "mismatch" + return result + + # Verify the chain if we have a source file + if claim.source_file: + from ._chain import verify_chain + + try: + chain = verify_chain(claim.source_file) + result["chain_verified"] = chain.is_verified + if chain.is_verified: + result["details"].append(f"Chain verified ({len(chain.runs)} runs)") + else: + result["details"].append( + f"Chain verification failed ({len(chain.failed_runs)} failed runs)" + ) + except Exception as e: + result["details"].append(f"Chain verification error: {e}") + + # Update status + if result["source_verified"] and result["chain_verified"]: + _update_claim_status(claim.claim_id, "verified", db) + result["claim"]["status"] = "verified" + elif result["source_verified"]: + _update_claim_status(claim.claim_id, "partial", db) + result["claim"]["status"] = "partial" + + return result
+ + + +def verify_claims_dag( + file_path: Optional[str] = None, + claim_type: Optional[str] = None, +) -> DAGVerification: + """Build a unified DAG from all claims, tracing each back to its source. + + Parameters + ---------- + file_path : str, optional + Filter claims by manuscript file path. + claim_type : str, optional + Filter claims by type. + + Returns + ------- + DAGVerification + Unified verification result covering all claim source chains merged. + """ + from ._chain import DAGVerification, VerificationStatus + from ._dag import verify_dag + + claims = list_claims(file_path=file_path, claim_type=claim_type) + + # Collect unique source files from claims + source_files = [] + for c in claims: + if c.source_file and c.source_file not in source_files: + source_files.append(c.source_file) + + if not source_files: + return DAGVerification( + target_files=[], + runs=[], + edges=[], + status=VerificationStatus.UNKNOWN, + topological_order=[], + ) + + return verify_dag(source_files) + + +def _resolve_claim(identifier: str, db) -> Optional[Claim]: + """Resolve a claim by ID or location string.""" + conn = sqlite3.connect(str(db.db_path)) + conn.row_factory = sqlite3.Row + try: + # Try claim_id first + row = conn.execute( + "SELECT * FROM claims WHERE claim_id = ?", (identifier,) + ).fetchone() + + if not row: + # Try location format: file.tex:L42 + match = re.match(r"^(.+):L(\d+)$", identifier) + if match: + fpath = str(Path(match.group(1)).resolve()) + line = int(match.group(2)) + row = conn.execute( + "SELECT * FROM claims WHERE file_path = ? AND line_number = ?", + (fpath, line), + ).fetchone() + + if not row: + # Try file path only (returns first match) + fpath = str(Path(identifier).resolve()) + row = conn.execute( + "SELECT * FROM claims WHERE file_path = ? ORDER BY line_number LIMIT 1", + (fpath,), + ).fetchone() + + if row: + return Claim( + claim_id=row["claim_id"], + file_path=row["file_path"], + line_number=row["line_number"], + claim_type=row["claim_type"], + claim_value=row["claim_value"], + source_session=row["source_session"], + source_file=row["source_file"], + source_hash=row["source_hash"], + registered_at=row["registered_at"], + verified_at=row["verified_at"], + status=row["status"], + ) + return None + finally: + conn.close() + + +def _update_claim_status(claim_id: str, status: str, db) -> None: + """Update claim verification status.""" + conn = sqlite3.connect(str(db.db_path)) + try: + conn.execute( + "UPDATE claims SET status = ?, verified_at = ? WHERE claim_id = ?", + (status, datetime.now().isoformat(), claim_id), + ) + conn.commit() + finally: + conn.close() + + +def _ensure_claims_table(db) -> None: + """Ensure the claims table exists (run migration).""" + migrate_add_claims_table(db.db_path) + + +def format_claims(claims: List[Claim], verbose: bool = False) -> str: + """Format claims list for terminal display.""" + if not claims: + return "No claims registered." + + lines = [] + status_icons = { + "registered": "\u25cb", # ○ + "verified": "\u2713", # ✓ + "mismatch": "\u2717", # ✗ + "missing": "?", + "partial": "~", + } + + for c in claims: + icon = status_icons.get(c.status, "?") + loc = c.location + val = f" = {c.claim_value}" if c.claim_value else "" + lines.append(f" {icon} [{c.claim_type}] {loc}{val}") + if verbose and c.source_file: + src = Path(c.source_file).name + lines.append( + f" source: {src} (session: {c.source_session or 'unknown'})" + ) + + return "\n".join(lines) + + +__all__ = [ + "CLAIM_TYPES", + "Claim", + "add_claim", + "list_claims", + "verify_claim", + "verify_claims_dag", + "format_claims", + "migrate_add_claims_table", +] +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_clew/_examples.html b/src/scitex/_sphinx_html/_modules/scitex_clew/_examples.html new file mode 100644 index 000000000..e6cf5a0ff --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_clew/_examples.html @@ -0,0 +1,775 @@ + + + + + + + + scitex_clew._examples — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_clew._examples

+#!/usr/bin/env python3
+"""Create Clew example pipeline in a target directory."""
+
+from __future__ import annotations
+
+import shutil
+from pathlib import Path
+from typing import Optional
+
+
+def _find_examples_dir(variant: str = "sequential") -> Optional[Path]:
+    """Locate the bundled Clew examples directory.
+
+    Tries (in order):
+    1. Source repo: ../../../examples/<variant>/   (editable install or git clone)
+    2. Docker mount: /scitex-python/examples/scitex/clew
+    """
+    # __file__ = <repo>/src/scitex_clew/_examples.py — 3 parents up = <repo>.
+    repo_root = Path(__file__).resolve().parents[2]
+    candidates = [
+        repo_root / "examples" / variant,
+        Path("/scitex-python/examples/scitex/clew") / variant,
+    ]
+    for candidate in candidates:
+        if candidate.exists() and candidate.is_dir():
+            return candidate
+    return None
+
+
+
+[docs] +def init_examples( + dest: str | Path, + variant: str = "sequential", + *, + find_examples_dir=_find_examples_dir, +) -> dict: + """Copy Clew example scripts to a destination directory. + + Copies only the runnable scripts (.py, .sh) and README — not + the output directories. Users run ``00_run_all.sh`` themselves + to generate outputs and populate the verification database. + + Parameters + ---------- + dest : str or Path + Destination directory. Created if it does not exist. + Existing script files are overwritten. + variant : str, optional + Example variant: "sequential" (default) or "multi_parent". + find_examples_dir : callable, optional + Locator callable ``(variant: str) -> Optional[Path]`` used to + resolve the bundled examples source. Production callers should + not pass this; it is the canonical PA-306 §1 DI seam — tests + inject a hand-rolled fake that returns a ``tmp_path``-rooted + directory or ``None``. + + Returns + ------- + dict + ``{"path": str, "files": list[str], "file_count": int, "variant": str}`` + + Raises + ------ + FileNotFoundError + If the bundled examples cannot be located. + ValueError + If variant is not recognized. + """ + valid_variants = ("sequential", "multi_parent") + if variant not in valid_variants: + raise ValueError(f"Unknown variant {variant!r}. Choose from: {valid_variants}") + + src_dir = find_examples_dir(variant) + if src_dir is None: + raise FileNotFoundError( + f"Clew example variant {variant!r} not found. " + "Examples live at ~/proj/scitex-clew/examples/<variant>/ — " + "install editably (`pip install -e .`) or clone the repo." + ) + + dest = Path(dest) + dest.mkdir(parents=True, exist_ok=True) + + # Copy only scripts and docs, skip _out directories + copied = [] + for src_file in sorted(src_dir.iterdir()): + if src_file.is_dir(): + continue + dst_file = dest / src_file.name + shutil.copy2(src_file, dst_file) + copied.append(src_file.name) + + return { + "path": str(dest), + "files": copied, + "file_count": len(copied), + "variant": variant, + }
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_clew/_hash.html b/src/scitex/_sphinx_html/_modules/scitex_clew/_hash.html new file mode 100644 index 000000000..525045c33 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_clew/_hash.html @@ -0,0 +1,865 @@ + + + + + + + + scitex_clew._hash — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_clew._hash

+#!/usr/bin/env python3
+# Timestamp: "2026-02-01 (ywatanabe)"
+# File: /home/ywatanabe/proj/scitex-python/src/scitex/verify/_hash.py
+"""File and directory hashing utilities for verification."""
+
+from __future__ import annotations
+
+import hashlib
+from pathlib import Path
+from typing import Dict, Union
+
+
+
+[docs] +def hash_file( + path: Union[str, Path], + algorithm: str = "sha256", + chunk_size: int = 8192, +) -> str: + """ + Compute hash of a file. + + Parameters + ---------- + path : str or Path + Path to the file to hash + algorithm : str, optional + Hash algorithm (default: sha256) + chunk_size : int, optional + Size of chunks to read (default: 8192) + + Returns + ------- + str + Hexadecimal hash string (first 32 characters) + + Examples + -------- + >>> hash_file("data.csv") + 'a1b2c3d4e5f6...' + """ + path = Path(path) + if not path.exists(): + raise FileNotFoundError(f"File not found: {path}") + + hasher = hashlib.new(algorithm) + with open(path, "rb") as f: + while chunk := f.read(chunk_size): + hasher.update(chunk) + + return hasher.hexdigest()[:32]
+ + + +
+[docs] +def hash_directory( + path: Union[str, Path], + pattern: str = "*", + recursive: bool = True, + algorithm: str = "sha256", +) -> Dict[str, str]: + """ + Compute hashes for all files in a directory. + + Parameters + ---------- + path : str or Path + Directory path + pattern : str, optional + Glob pattern for files (default: "*") + recursive : bool, optional + Whether to search recursively (default: True) + algorithm : str, optional + Hash algorithm (default: sha256) + + Returns + ------- + dict + Mapping of relative paths to hashes + + Examples + -------- + >>> hash_directory("./data/") + {'input.csv': 'a1b2...', 'config.yaml': 'c3d4...'} + """ + path = Path(path) + if not path.is_dir(): + raise NotADirectoryError(f"Not a directory: {path}") + + glob_method = path.rglob if recursive else path.glob + hashes = {} + + for file_path in glob_method(pattern): + if file_path.is_file(): + rel_path = str(file_path.relative_to(path)) + hashes[rel_path] = hash_file(file_path, algorithm=algorithm) + + return hashes
+ + + +def hash_files( + paths: list[Union[str, Path]], + algorithm: str = "sha256", +) -> Dict[str, str]: + """ + Compute hashes for a list of files. + + Parameters + ---------- + paths : list of str or Path + List of file paths + algorithm : str, optional + Hash algorithm (default: sha256) + + Returns + ------- + dict + Mapping of paths to hashes + """ + hashes = {} + for path in paths: + path = Path(path) + if path.exists() and path.is_file(): + hashes[str(path)] = hash_file(path, algorithm=algorithm) + return hashes + + +def combine_hashes(hashes: Dict[str, str], algorithm: str = "sha256") -> str: + """ + Combine multiple hashes into a single hash. + + Creates a deterministic combined hash from a dictionary of hashes. + + Parameters + ---------- + hashes : dict + Mapping of names to hashes + algorithm : str, optional + Hash algorithm (default: sha256) + + Returns + ------- + str + Combined hash (first 32 characters) + + Examples + -------- + >>> hashes = {'input.csv': 'a1b2...', 'script.py': 'c3d4...'} + >>> combine_hashes(hashes) + 'e5f6g7h8...' + """ + hasher = hashlib.new(algorithm) + + # Sort by key for deterministic ordering + for key in sorted(hashes.keys()): + hasher.update(f"{key}:{hashes[key]}".encode()) + + return hasher.hexdigest()[:32] + + +def verify_hash( + path: Union[str, Path], + expected_hash: str, + algorithm: str = "sha256", +) -> bool: + """ + Verify that a file matches an expected hash. + + Parameters + ---------- + path : str or Path + Path to the file + expected_hash : str + Expected hash value + algorithm : str, optional + Hash algorithm (default: sha256) + + Returns + ------- + bool + True if hash matches, False otherwise + """ + try: + actual_hash = hash_file(path, algorithm=algorithm) + # Compare only the length of expected_hash (may be truncated) + return actual_hash[: len(expected_hash)] == expected_hash + except FileNotFoundError: + return False + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_clew/_observers/_session.html b/src/scitex/_sphinx_html/_modules/scitex_clew/_observers/_session.html new file mode 100644 index 000000000..3f670c64c --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_clew/_observers/_session.html @@ -0,0 +1,792 @@ + + + + + + + + scitex_clew._observers._session — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_clew._observers._session

+#!/usr/bin/env python3
+"""Session lifecycle hooks for scitex-clew.
+
+These thin wrappers are invoked by ``@scitex.session`` (or any equivalent
+session manager) at the start and end of a run. They delegate to the
+``scitex_clew._tracker`` machinery so that a run record is opened on start
+and finalized (with a combined hash) on close.
+
+They import only scitex-clew internals; no scitex-io dependency is needed.
+"""
+
+from __future__ import annotations
+
+import os
+from typing import Optional
+
+from .._logging import getLogger
+from .._tracker import get_tracker, start_tracking, stop_tracking
+
+logger = getLogger(__name__)
+
+
+
+[docs] +def on_session_start( + session_id: str, + script_path: Optional[str] = None, + parent_session: Optional[str] = None, + verbose: bool = False, + metadata: Optional[dict] = None, +) -> None: + """ + Hook called when a session starts. + + Parameters + ---------- + session_id : str + Unique session identifier + script_path : str, optional + Path to the script being run + parent_session : str, optional + Parent session ID for chain tracking + verbose : bool, optional + Whether to log status messages + metadata : dict, optional + Additional metadata (e.g. notebook_path, cell_index) + """ + try: + start_tracking( + session_id=session_id, + script_path=script_path, + parent_session=parent_session, + metadata=metadata, + ) + except Exception as e: + if verbose: + logger.warning(f"Could not start verification tracking: {e}")
+ + + +
+[docs] +def on_session_close( + status: str = "success", + exit_code: int = 0, + verbose: bool = False, + register: Optional[bool] = None, +) -> None: + """ + Hook called when a session closes. + + Parameters + ---------- + status : str, optional + Final status (success, failed, error) + exit_code : int, optional + Exit code of the script + verbose : bool, optional + Whether to log status messages + register : bool, optional + If True, register session hashes with remote Clew Registry. + If None, checks SCITEX_AUTO_REGISTER environment variable. + """ + try: + tracker = get_tracker() + stop_tracking(status=status, exit_code=exit_code) + if _should_auto_register(register) and tracker is not None: + _auto_register_session(tracker.session_id) + except Exception as e: + if verbose: + logger.warning(f"Could not stop verification tracking: {e}")
+ + + +# ── Registry helpers ── + + +def _should_auto_register(register: Optional[bool]) -> bool: + """Check whether auto-registration is enabled.""" + if register is not None: + return register + + return os.environ.get("SCITEX_AUTO_REGISTER", "").lower() in ( + "1", + "true", + "yes", + ) + + +def _auto_register_session(session_id: str) -> None: + """Register session hashes with remote Clew Registry (fire-and-forget).""" + try: + from .._registry import get_registry + + get_registry().register_session(session_id) + except Exception as e: + logger.debug("clew: failed to auto-register session %s: %s", session_id, e) + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_clew/_register_intermediate.html b/src/scitex/_sphinx_html/_modules/scitex_clew/_register_intermediate.html new file mode 100644 index 000000000..4a5ab25af --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_clew/_register_intermediate.html @@ -0,0 +1,775 @@ + + + + + + + + scitex_clew._register_intermediate — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_clew._register_intermediate

+#!/usr/bin/env python3
+"""Register an in-session intermediate value as a Clew claim.
+
+Wrapper around `_claim.add_claim` for the agentic-reasoning use case
+(see `_skills/scitex-clew/20_agentic-reasoning.md`). Lets an AI agent or
+script register a computed intermediate value without needing to construct
+a manuscript file path or line number — uses the active session's log file
+as the synthetic source.
+"""
+
+from __future__ import annotations
+
+import json
+import os
+import sys
+from typing import Any, List, Optional
+
+from ._claim import Claim, add_claim
+
+
+
+[docs] +def register_intermediate( + name: str, + value: Any, + supports: Optional[List[str]] = None, + session_id: Optional[str] = None, + claim_type: str = "value", +) -> Claim: + """Register a computed intermediate as a Clew claim. + + Use this from inside a `@stx.session` script (or from an agent loop) to + record any non-trivial intermediate value with explicit upstream support. + The claim becomes part of the DAG and can be queried via `clew.chain`, + `clew.dag`, or the MCP `clew_chain` / `clew_dag` tools. + + Parameters + ---------- + name + Descriptive identifier (e.g. `"acute_n_sig_pathways"`). Avoid generic + names like `"result_3"` — the id is the only handle a future inspector + has on the value. + value + The computed result. Coerced to string for storage; the hash chain + sees `repr(value)` so types matter. + supports + List of upstream claim ids or session ids that this value depends on. + Stored as JSON in the claim's value field for retrieval. None means + no explicit upstream (use sparingly). + session_id + The session this value belongs to. If None, read from the + `SCITEX_SESSION_ID` env var that `@stx.session` sets at start. + claim_type + One of `statistic`, `figure`, `table`, `text`, `value`. Defaults to + `value` since intermediates are usually scalar / categorical results. + + Returns + ------- + Claim + The registered claim object. + + Raises + ------ + ValueError + If no session_id can be determined (env var unset and not passed). + + Examples + -------- + Inside a `@stx.session` script: + + >>> from scitex_clew import register_intermediate + >>> n_sig = sum(1 for p in pathways if p.padj < 0.05) + >>> register_intermediate( + ... name="chronic_r2_n_sig_pathways", + ... value=n_sig, + ... supports=["chronic_r2_min_pvals", "reactome_pathways_v2024"], + ... ) + """ + if session_id is None: + session_id = os.environ.get("SCITEX_SESSION_ID") + if not session_id: + raise ValueError( + "register_intermediate: no session_id given and SCITEX_SESSION_ID " + "is not set in the environment. Either pass session_id explicitly " + "or run inside a @stx.session-decorated script." + ) + + payload = { + "name": name, + "value": repr(value), + "supports": list(supports) if supports else [], + } + + script_path = sys.argv[0] if sys.argv else "<agent>" + return add_claim( + file_path=script_path, + claim_type=claim_type, + line_number=None, + claim_value=json.dumps(payload, sort_keys=True), + source_file=None, + source_session=session_id, + )
+ +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_clew/_rerun.html b/src/scitex/_sphinx_html/_modules/scitex_clew/_rerun.html new file mode 100644 index 000000000..c550c6d97 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_clew/_rerun.html @@ -0,0 +1,1085 @@ + + + + + + + + scitex_clew._rerun — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_clew._rerun

+#!/usr/bin/env python3
+# Timestamp: "2026-02-01 (ywatanabe)"
+# File: /home/ywatanabe/proj/scitex-python/src/scitex/verify/_rerun.py
+"""Rerun verification - re-execute scripts and compare outputs."""
+
+from __future__ import annotations
+
+import dataclasses
+import shutil
+import subprocess
+from pathlib import Path
+from typing import Dict
+
+from ._chain import (
+    DAGVerification,
+    FileVerification,
+    RunVerification,
+    VerificationLevel,
+    VerificationStatus,
+)
+from ._db import get_db
+
+
+def verify_by_rerun(
+    target: str | list[str],
+    timeout: int = 300,
+    cleanup: bool = True,
+) -> RunVerification | list[RunVerification]:
+    """
+    Verify session(s) by re-executing scripts and comparing outputs.
+
+    Parameters
+    ----------
+    target : str or list[str]
+        Session ID, script path, or artifact path.
+        - run_id: directly use this run
+        - script path: latest run that executed this script
+        - artifact path: latest run which produced this file
+    timeout : int, optional
+        Maximum execution time in seconds (default: 300)
+    cleanup : bool, optional
+        Whether to remove the new session's output directory after verification
+
+    Returns
+    -------
+    RunVerification or list[RunVerification]
+        Single result if single target, list if multiple targets
+    """
+    if isinstance(target, list):
+        return [_verify_single(t, timeout, cleanup) for t in target]
+    return _verify_single(target, timeout, cleanup)
+
+
+def _verify_single(
+    target: str,
+    timeout: int = 300,
+    cleanup: bool = True,
+) -> RunVerification:
+    """Verify a single target."""
+    db = get_db()
+
+    # Resolve target to session_id
+    session_id = _resolve_to_session_id(db, target)
+    if not session_id:
+        return _unknown_result(target, None)
+
+    # Get original run info
+    run_info = db.get_run(session_id)
+    if not run_info:
+        return _unknown_result(session_id, None)
+
+    script_path = run_info.get("script_path")
+    if not script_path or not Path(script_path).exists():
+        return RunVerification(
+            session_id=session_id,
+            script_path=script_path,
+            status=VerificationStatus.MISSING,
+            files=[],
+            combined_hash_expected=None,
+            combined_hash_current=None,
+            level=VerificationLevel.RERUN,
+        )
+
+    # Get expected output hashes from original session
+    original_hashes = db.get_file_hashes(session_id, role="output")
+    if not original_hashes:
+        return _unknown_result(session_id, script_path)
+
+    # Re-execute the script (creates new session)
+    exec_result = _execute_script(script_path, timeout)
+    if exec_result is not None:
+        return dataclasses.replace(exec_result, session_id=session_id)
+
+    # Find the new session (most recent from this script)
+    new_session_id, new_sdir_run = _find_new_session(db, script_path, session_id)
+    if not new_session_id:
+        return _unknown_result(session_id, script_path)
+
+    # Get new session's output hashes
+    new_hashes = db.get_file_hashes(new_session_id, role="output")
+
+    # Compare hashes by filename
+    file_verifications = _compare_hashes(original_hashes, new_hashes)
+
+    # Cleanup new session's output directory if requested
+    if cleanup and new_sdir_run:
+        _cleanup_session_dir(new_sdir_run)
+
+    # Determine overall status
+    status = _determine_status(file_verifications)
+
+    # Record verification result in database for original session
+    db.record_verification(
+        session_id=session_id,
+        level=VerificationLevel.RERUN.value,
+        status=status.value,
+    )
+
+    return RunVerification(
+        session_id=session_id,
+        script_path=script_path,
+        status=status,
+        files=file_verifications,
+        combined_hash_expected=run_info.get("combined_hash"),
+        combined_hash_current=None,
+        level=VerificationLevel.RERUN,
+    )
+
+
+def _resolve_to_session_id(db, target: str) -> str | None:
+    """Resolve target to session_id.
+
+    Accepts:
+        - run_id: directly use this run
+        - script path: latest run that executed this script
+        - artifact path: latest run which produced this file
+    """
+    # Try as run_id
+    if db.get_run(target):
+        return target
+
+    # Always resolve to absolute path
+    resolved = str(Path(target).resolve())
+
+    # Try as script path
+    for run in db.list_runs(limit=100):
+        if run.get("script_path") == resolved:
+            return run["session_id"]
+
+    # Try as artifact (output) path
+    sessions = db.find_session_by_file(resolved, role="output")
+    return sessions[0] if sessions else None
+
+
+def _unknown_result(session_id: str, script_path: str) -> RunVerification:
+    """Create an unknown verification result."""
+    return RunVerification(
+        session_id=session_id,
+        script_path=script_path,
+        status=VerificationStatus.UNKNOWN,
+        files=[],
+        combined_hash_expected=None,
+        combined_hash_current=None,
+        level=VerificationLevel.RERUN,
+    )
+
+
+def _execute_script(script_path: str, timeout: int) -> RunVerification | None:
+    """Execute script and return error result if failed, None if success."""
+    try:
+        result = subprocess.run(
+            ["python", script_path],
+            capture_output=True,
+            timeout=timeout,
+            cwd=Path(script_path).parent,
+        )
+        if result.returncode != 0:
+            return RunVerification(
+                session_id="",
+                script_path=script_path,
+                status=VerificationStatus.MISMATCH,
+                files=[],
+                combined_hash_expected=None,
+                combined_hash_current=None,
+                level=VerificationLevel.RERUN,
+            )
+        return None  # Success
+    except subprocess.TimeoutExpired:
+        return _unknown_result("", script_path)
+    except Exception:
+        return _unknown_result("", script_path)
+
+
+def _find_new_session(db, script_path: str, original_id: str) -> tuple:
+    """Find the new session created by re-running the script."""
+    recent_runs = db.list_runs(limit=5)
+    for run in recent_runs:
+        if run.get("script_path") == script_path and run["session_id"] != original_id:
+            return run["session_id"], run.get("sdir_run")
+    return None, None
+
+
+def _compare_hashes(
+    original_hashes: Dict[str, str], new_hashes: Dict[str, str]
+) -> list:
+    """Compare hashes by filename and return FileVerification list."""
+    original_by_name = {Path(p).name: h for p, h in original_hashes.items()}
+    new_by_name = {Path(p).name: h for p, h in new_hashes.items()}
+
+    verifications = []
+    for filename, expected_hash in original_by_name.items():
+        current_hash = new_by_name.get(filename)
+        if current_hash is None:
+            status = VerificationStatus.MISSING
+        elif current_hash == expected_hash:
+            status = VerificationStatus.VERIFIED
+        else:
+            status = VerificationStatus.MISMATCH
+
+        verifications.append(
+            FileVerification(
+                path=filename,
+                role="output",
+                expected_hash=expected_hash,
+                current_hash=current_hash,
+                status=status,
+            )
+        )
+    return verifications
+
+
+def _cleanup_session_dir(sdir_run: str) -> None:
+    """Remove the session's output directory (best-effort)."""
+    try:
+        path = Path(sdir_run)
+        if path.exists():
+            shutil.rmtree(path)
+    except Exception:
+        pass
+
+
+def _determine_status(file_verifications: list) -> VerificationStatus:
+    """Determine overall verification status from file verifications."""
+    if all(f.is_verified for f in file_verifications):
+        return VerificationStatus.VERIFIED
+    if any(f.status == VerificationStatus.MISMATCH for f in file_verifications):
+        return VerificationStatus.MISMATCH
+    return VerificationStatus.UNKNOWN
+
+
+
+[docs] +def rerun_dag( + targets: list[str] | None = None, + timeout: int = 300, + cleanup: bool = True, +) -> DAGVerification: + """Rerun-verify an entire DAG in topological order. + + Each session is re-executed in a sandbox against its ORIGINAL stored + inputs (not freshly rerun outputs from upstream), then compared to + the original outputs. + + Parameters + ---------- + targets : list of str, optional + Target output files whose upstream DAG should be rerun. + If None, all runs in the database are used and their output + files become the targets. + timeout : int, optional + Maximum execution time per session in seconds (default: 300). + cleanup : bool, optional + Whether to remove sandbox output directories after each rerun. + + Returns + ------- + DAGVerification + Unified verification result for the entire DAG. + """ + from ._chain import DAGVerification + from ._chain._dag import _topological_sort + + db = get_db() + + # If no targets, collect all output files from all runs + if targets is None: + targets = [] + for run in db.list_runs(limit=10000): + hashes = db.get_file_hashes(run["session_id"], role="output") + targets.extend(hashes.keys()) + + # Resolve targets to leaf session IDs + resolved_targets = [] + leaf_sessions = [] + for target in targets: + target_str = str(Path(target).resolve()) + resolved_targets.append(target_str) + sessions = db.find_session_by_file(target_str, role="output") + if sessions: + leaf_sessions.append(sessions[0]) + + if not leaf_sessions: + return DAGVerification( + target_files=resolved_targets, + runs=[], + edges=[], + status=VerificationStatus.UNKNOWN, + topological_order=[], + ) + + # BFS backward to collect full DAG + adjacency, _all_ids = db.get_dag(leaf_sessions) + + # Topological sort (roots first) + topo_order = _topological_sort(adjacency) + + # Rerun each session in topological order + verifications = {} + for sid in topo_order: + verifications[sid] = verify_by_rerun(sid, timeout, cleanup) + + # Propagate failures forward through the DAG + failed_sessions: set = set() + for sid in topo_order: + parents = adjacency.get(sid, []) + has_failed_parent = any(p in failed_sessions for p in parents) + if has_failed_parent or not verifications[sid].is_verified: + failed_sessions.add(sid) + + # Build edges list + edges = [] + for child, parents in adjacency.items(): + for p in parents: + edges.append((p, child)) + + # Determine overall status + run_list = [verifications[sid] for sid in topo_order] + + if all(sid not in failed_sessions for sid in topo_order): + status = VerificationStatus.VERIFIED + elif any( + verifications[sid].status == VerificationStatus.MISMATCH for sid in topo_order + ): + status = VerificationStatus.MISMATCH + elif any( + verifications[sid].status == VerificationStatus.MISSING for sid in topo_order + ): + status = VerificationStatus.MISSING + else: + status = VerificationStatus.UNKNOWN + + return DAGVerification( + target_files=resolved_targets, + runs=run_list, + edges=edges, + status=status, + topological_order=topo_order, + )
+ + + +
+[docs] +def rerun_claims( + file_path: str | None = None, + claim_type: str | None = None, + timeout: int = 300, + cleanup: bool = True, +) -> DAGVerification: + """Rerun-verify all sessions that produced files referenced by claims. + + Collects unique source files from matching claims, then delegates + to ``rerun_dag`` with those files as targets. + + Parameters + ---------- + file_path : str, optional + Filter claims by manuscript file path. + claim_type : str, optional + Filter claims by type (statistic, figure, table, text, value). + timeout : int, optional + Maximum execution time per session in seconds (default: 300). + cleanup : bool, optional + Whether to remove sandbox output directories after each rerun. + + Returns + ------- + DAGVerification + Unified verification result for the upstream DAG of all + source files referenced by the matching claims. + """ + from ._claim import list_claims + + claims = list_claims(file_path=file_path, claim_type=claim_type) + + # Collect unique source files from matching claims + source_files = [] + seen = set() + for claim in claims: + sf = claim.source_file + if sf and sf not in seen: + seen.add(sf) + source_files.append(sf) + + return rerun_dag(source_files or None, timeout, cleanup)
+ + + +# Backward compatibility alias +verify_run_from_scratch = verify_by_rerun + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_clew/_stamp.html b/src/scitex/_sphinx_html/_modules/scitex_clew/_stamp.html new file mode 100644 index 000000000..f25f63a09 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_clew/_stamp.html @@ -0,0 +1,1096 @@ + + + + + + + + scitex_clew._stamp — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_clew._stamp

+#!/usr/bin/env python3
+# Timestamp: "2026-02-09 (ywatanabe)"
+# File: /home/ywatanabe/proj/scitex-python/src/scitex/verify/_stamp.py
+"""External hash timestamping for temporal integrity.
+
+Provides independent temporal proof that a verification chain was consistent
+at a specific point in time. Only hashes are transmitted — never actual data.
+
+Backends (increasing trust level):
+  - file:    Local JSON file with timestamp (development/testing)
+  - rfc3161: RFC 3161 Timestamping Authority (production standard)
+  - zenodo:  Zenodo deposit with DOI (archival, citable)
+"""
+
+from __future__ import annotations
+
+import hashlib
+import json
+import sqlite3
+from dataclasses import dataclass
+from datetime import datetime, timezone
+from pathlib import Path
+from typing import Dict, List, Optional
+
+from ._db import get_db
+
+STAMP_BACKENDS = ("file", "rfc3161", "zenodo", "scitex_cloud")
+
+
+@dataclass
+class Stamp:
+    """A temporal proof record."""
+
+    stamp_id: str
+    root_hash: str
+    timestamp: str
+    backend: str
+    service_url: Optional[str]
+    response_token: Optional[str]
+    run_count: int
+    metadata: Optional[Dict] = None
+
+    def to_dict(self) -> Dict:
+        return {
+            "stamp_id": self.stamp_id,
+            "root_hash": self.root_hash,
+            "timestamp": self.timestamp,
+            "backend": self.backend,
+            "service_url": self.service_url,
+            "response_token": self.response_token,
+            "run_count": self.run_count,
+            "metadata": self.metadata,
+        }
+
+
+def migrate_add_stamps_table(db_path: Path) -> None:
+    """Create stamps table if not present. Safe to call multiple times."""
+    conn = sqlite3.connect(str(db_path))
+    try:
+        conn.execute(
+            """
+            CREATE TABLE IF NOT EXISTS stamps (
+                id INTEGER PRIMARY KEY AUTOINCREMENT,
+                stamp_id TEXT UNIQUE NOT NULL,
+                root_hash TEXT NOT NULL,
+                timestamp TEXT NOT NULL,
+                backend TEXT NOT NULL,
+                service_url TEXT,
+                response_token TEXT,
+                run_count INTEGER,
+                metadata TEXT
+            )
+            """
+        )
+        conn.execute("CREATE INDEX IF NOT EXISTS idx_stamps_hash ON stamps(root_hash)")
+        conn.commit()
+    finally:
+        conn.close()
+
+
+def compute_root_hash(session_ids: Optional[List[str]] = None) -> Dict:
+    """Compute a Merkle-like root hash over all (or selected) runs.
+
+    The root hash combines all run combined_hashes in deterministic order,
+    providing a single fingerprint for the entire verification state.
+
+    Parameters
+    ----------
+    session_ids : list of str, optional
+        Specific sessions to include. If None, includes all successful runs.
+
+    Returns
+    -------
+    dict
+        {root_hash, run_count, session_ids}
+    """
+    db = get_db()
+    conn = sqlite3.connect(str(db.db_path))
+    conn.row_factory = sqlite3.Row
+    try:
+        if session_ids:
+            placeholders = ",".join("?" * len(session_ids))
+            rows = conn.execute(
+                f"SELECT session_id, combined_hash FROM runs "
+                f"WHERE session_id IN ({placeholders}) "
+                f"ORDER BY session_id",
+                session_ids,
+            ).fetchall()
+        else:
+            rows = conn.execute(
+                "SELECT session_id, combined_hash FROM runs "
+                "WHERE status = 'success' AND combined_hash IS NOT NULL "
+                "ORDER BY session_id"
+            ).fetchall()
+
+        if not rows:
+            return {"root_hash": None, "run_count": 0, "session_ids": []}
+
+        hasher = hashlib.sha256()
+        ids = []
+        for row in rows:
+            hasher.update(row["session_id"].encode())
+            hasher.update((row["combined_hash"] or "").encode())
+            ids.append(row["session_id"])
+
+        return {
+            "root_hash": hasher.hexdigest(),
+            "run_count": len(ids),
+            "session_ids": ids,
+        }
+    finally:
+        conn.close()
+
+
+
+[docs] +def stamp( + backend: str = "file", + service_url: Optional[str] = None, + session_ids: Optional[List[str]] = None, + output_dir: Optional[str] = None, +) -> Stamp: + """Record root hash with external timestamp. + + Parameters + ---------- + backend : str + One of: file, rfc3161, zenodo. + service_url : str, optional + URL for RFC 3161 TSA or Zenodo API. + session_ids : list of str, optional + Specific sessions to stamp. If None, stamps all successful runs. + output_dir : str, optional + Directory for file-based stamps (default: <db_dir>/stamps, i.e. .scitex/clew/runtime/stamps/). + + Returns + ------- + Stamp + The timestamp proof record. + """ + if backend not in STAMP_BACKENDS: + raise ValueError( + f"Invalid backend '{backend}'. Must be one of: {STAMP_BACKENDS}" + ) + + root = compute_root_hash(session_ids) + if not root["root_hash"]: + raise ValueError("No runs to stamp (no successful runs with combined hashes)") + + now = datetime.now(timezone.utc).isoformat() + root_hash = root["root_hash"] + raw = f"{root_hash}:{now}" + stamp_id = f"stamp_{hashlib.sha256(raw.encode()).hexdigest()[:12]}" + + if backend == "file": + result = _stamp_file(stamp_id, root, now, output_dir) + elif backend == "rfc3161": + result = _stamp_rfc3161(stamp_id, root, now, service_url) + elif backend == "zenodo": + result = _stamp_zenodo(stamp_id, root, now, service_url) + elif backend == "scitex_cloud": + result = _stamp_scitex_cloud(stamp_id, root, now, service_url) + else: + raise ValueError(f"Unsupported backend: {backend}") + + stamp_obj = Stamp( + stamp_id=stamp_id, + root_hash=root["root_hash"], + timestamp=now, + backend=backend, + service_url=result.get("service_url"), + response_token=result.get("response_token"), + run_count=root["run_count"], + metadata={"session_ids": root["session_ids"]}, + ) + + # Store in database + db = get_db() + _ensure_stamps_table(db) + conn = sqlite3.connect(str(db.db_path)) + try: + conn.execute( + """ + INSERT INTO stamps + (stamp_id, root_hash, timestamp, backend, service_url, + response_token, run_count, metadata) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + """, + ( + stamp_obj.stamp_id, + stamp_obj.root_hash, + stamp_obj.timestamp, + stamp_obj.backend, + stamp_obj.service_url, + stamp_obj.response_token, + stamp_obj.run_count, + json.dumps(stamp_obj.metadata), + ), + ) + conn.commit() + finally: + conn.close() + + return stamp_obj
+ + + +
+[docs] +def check_stamp(stamp_id: Optional[str] = None) -> Dict: + """Verify a stamp against current verification state. + + Parameters + ---------- + stamp_id : str, optional + Specific stamp to check. If None, checks the latest stamp. + + Returns + ------- + dict + {stamp, current_root_hash, matches, details} + """ + db = get_db() + _ensure_stamps_table(db) + + conn = sqlite3.connect(str(db.db_path)) + conn.row_factory = sqlite3.Row + try: + if stamp_id: + row = conn.execute( + "SELECT * FROM stamps WHERE stamp_id = ?", (stamp_id,) + ).fetchone() + else: + row = conn.execute( + "SELECT * FROM stamps ORDER BY id DESC LIMIT 1" + ).fetchone() + + if not row: + return {"status": "not_found", "message": "No stamps found"} + + stored_stamp = Stamp( + stamp_id=row["stamp_id"], + root_hash=row["root_hash"], + timestamp=row["timestamp"], + backend=row["backend"], + service_url=row["service_url"], + response_token=row["response_token"], + run_count=row["run_count"], + metadata=json.loads(row["metadata"]) if row["metadata"] else None, + ) + + # Recompute root hash from the same sessions + session_ids = ( + stored_stamp.metadata.get("session_ids") if stored_stamp.metadata else None + ) + current = compute_root_hash(session_ids) + + matches = current["root_hash"] == stored_stamp.root_hash + details = [] + + if matches: + details.append(f"Root hash matches stamp from {stored_stamp.timestamp}") + else: + details.append(f"Root hash CHANGED since stamp at {stored_stamp.timestamp}") + details.append(f" Stamped: {stored_stamp.root_hash[:32]}...") + details.append(f" Current: {current['root_hash'][:32]}...") + + if current["run_count"] != stored_stamp.run_count: + details.append( + f" Run count changed: {stored_stamp.run_count}{current['run_count']}" + ) + + return { + "stamp": stored_stamp.to_dict(), + "current_root_hash": current["root_hash"], + "matches": matches, + "details": details, + } + finally: + conn.close()
+ + + +
+[docs] +def list_stamps(limit: int = 20) -> List[Stamp]: + """List all stamps.""" + db = get_db() + _ensure_stamps_table(db) + + conn = sqlite3.connect(str(db.db_path)) + conn.row_factory = sqlite3.Row + try: + rows = conn.execute( + "SELECT * FROM stamps ORDER BY id DESC LIMIT ?", (limit,) + ).fetchall() + return [ + Stamp( + stamp_id=r["stamp_id"], + root_hash=r["root_hash"], + timestamp=r["timestamp"], + backend=r["backend"], + service_url=r["service_url"], + response_token=r["response_token"], + run_count=r["run_count"], + metadata=json.loads(r["metadata"]) if r["metadata"] else None, + ) + for r in rows + ] + finally: + conn.close()
+ + + +# ── Backend implementations ── + + +def _stamp_file(stamp_id, root, timestamp, output_dir=None): + """File-based stamping: write JSON proof to local directory.""" + if output_dir: + stamp_dir = Path(output_dir) + else: + db = get_db() + stamp_dir = db.db_path.parent / "stamps" + + stamp_dir.mkdir(parents=True, exist_ok=True) + stamp_path = stamp_dir / f"{stamp_id}.json" + + proof = { + "stamp_id": stamp_id, + "root_hash": root["root_hash"], + "timestamp": timestamp, + "run_count": root["run_count"], + "backend": "file", + } + + stamp_path.write_text(json.dumps(proof, indent=2)) + return {"service_url": str(stamp_path), "response_token": None} + + +def _stamp_rfc3161(stamp_id, root, timestamp, service_url=None): + """RFC 3161 Timestamping Authority.""" + try: + import rfc3161ng + except ImportError: + raise ImportError( + "RFC 3161 stamping requires 'rfc3161ng' package. " + "Install with: pip install rfc3161ng" + ) + + url = service_url or "http://zeitstempel.dfn.de" + certificate = rfc3161ng.RemoteTimestamper(url) + + hash_bytes = bytes.fromhex(root["root_hash"]) + tst = certificate.timestamp(data=hash_bytes) + + token_hex = tst.hex() if isinstance(tst, bytes) else str(tst) + return {"service_url": url, "response_token": token_hex[:256]} + + +def _stamp_zenodo(stamp_id, root, timestamp, service_url=None): + """Zenodo deposit: create a record with the root hash.""" + raise NotImplementedError( + "Zenodo stamping is planned for a future release. " + "Use 'file' or 'rfc3161' backend instead." + ) + + +def _stamp_scitex_cloud(stamp_id, root, timestamp, service_url=None): + """SciTeX Cloud registry: register root hash with server-side timestamp.""" + from ._registry import get_registry + + registry = get_registry(base_url=service_url) + result = registry.register( + root["root_hash"], + source_type="stamp", + metadata={ + "stamp_id": stamp_id, + "run_count": root["run_count"], + "timestamp": timestamp, + }, + ) + + url = service_url or registry.base_url + token = ( + result.get("data", {}).get("registered_at") if result.get("success") else None + ) + return {"service_url": url, "response_token": token} + + +def _ensure_stamps_table(db) -> None: + """Ensure the stamps table exists.""" + migrate_add_stamps_table(db.db_path) + + +__all__ = [ + "STAMP_BACKENDS", + "Stamp", + "check_stamp", + "compute_root_hash", + "list_stamps", + "migrate_add_stamps_table", + "stamp", +] +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_compat/_compat.html b/src/scitex/_sphinx_html/_modules/scitex_compat/_compat.html new file mode 100644 index 000000000..fc670721c --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_compat/_compat.html @@ -0,0 +1,768 @@ + + + + + + + + scitex_compat._compat — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_compat._compat

+#!/usr/bin/env python3
+# File: src/scitex_compat/_compat.py
+
+"""Core deprecation decorator and compat shims.
+
+Implementation of the public surface re-exported from
+``scitex_compat.__init__`` — kept in its own module so the test suite can
+mirror it as ``tests/scitex_compat/test__compat.py``.
+"""
+
+from __future__ import annotations
+
+import warnings
+from functools import wraps
+from typing import Callable
+
+
+
+[docs] +def deprecated(new_name: str, removal_version: str = "2.0"): + """Decorator to mark functions as deprecated.""" + + def decorator(func: Callable) -> Callable: + @wraps(func) + def wrapper(*args, **kwargs): + warnings.warn( + f"{func.__name__} is deprecated. " + f"Use {new_name} instead. " + f"Will be removed in v{removal_version}.", + DeprecationWarning, + stacklevel=2, + ) + return func(*args, **kwargs) + + return wrapper + + return decorator
+ + + +
+[docs] +def notify(*args, **kwargs): + """Deprecated: Use scitex.notify.alert() instead. + + In standalone mode, this only emits a deprecation warning. + The actual notification requires scitex.notify to be installed. + """ + warnings.warn( + "scitex.compat.notify is deprecated. Use scitex.notify.alert instead.", + DeprecationWarning, + stacklevel=2, + ) + try: + from scitex.notify import alert + + return alert(*args, **kwargs) + except ImportError: + warnings.warn( + "scitex.notify is not installed. Notification not sent.", + RuntimeWarning, + stacklevel=2, + ) + return None
+ + + +
+[docs] +async def notify_async(*args, **kwargs): + """Deprecated: Use scitex.notify.alert_async() instead. + + In standalone mode, this only emits a deprecation warning. + The actual notification requires scitex.notify to be installed. + """ + warnings.warn( + "scitex.compat.notify_async is deprecated. Use scitex.notify.alert_async instead.", + DeprecationWarning, + stacklevel=2, + ) + try: + from scitex.notify import alert_async + + return await alert_async(*args, **kwargs) + except ImportError: + warnings.warn( + "scitex.notify is not installed. Notification not sent.", + RuntimeWarning, + stacklevel=2, + ) + return None
+ + + +__all__ = ["deprecated", "notify", "notify_async"] + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_config/_PriorityConfig.html b/src/scitex/_sphinx_html/_modules/scitex_config/_PriorityConfig.html new file mode 100644 index 000000000..3b26c7df5 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_config/_PriorityConfig.html @@ -0,0 +1,1061 @@ + + + + + + + + scitex_config._PriorityConfig — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for scitex_config._PriorityConfig

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Timestamp: "2025-12-09 (ywatanabe)"
+# File: /home/ywatanabe/proj/scitex-code/src/scitex/config/PriorityConfig.py
+
+
+"""
+Priority-based configuration resolver.
+
+Functionalities
+---------------
+- `PriorityConfig.resolve()` — precedence cascade `direct → config_dict → env → default`.
+- `load_dotenv()` — load `.env` file(s) into `os.environ` (cwd / $HOME / walk-up modes).
+- `get_scitex_dir()` — resolve `$SCITEX_DIR` (direct → env → `~/.scitex`).
+
+IO
+--
+- Reads: process env, `.env` files (cwd, `$HOME`, walk-up parents), config dicts.
+- Writes: `os.environ` (only keys not already set — process env wins).
+
+Dependencies
+------------
+- stdlib only (`os`, `pathlib`, `typing`).
+
+Based on priority-config by ywatanabe (https://github.com/ywatanabe1989/priority-config),
+incorporated into scitex for self-contained configuration management. Config-dict
+values (from YAML or passed dict) take priority over environment variables, following
+the Scholar module's CascadeConfig pattern.
+"""
+
+import os
+from pathlib import Path
+from typing import Any, Dict, List, Optional, Type, Union
+
+from ._env_loader import parse_src_file
+
+
+def _parse_dotenv_file(path: Path) -> bool:
+    """Parse a single .env file and merge into os.environ.
+
+    Line/value parsing (``export`` prefix, quote stripping, ``$VAR`` expansion)
+    is delegated to :func:`scitex_config._env_loader.parse_src_file` so the
+    ``.env`` and ``.src`` surfaces share one canonical parser.
+
+    Existing env vars are preserved (not overridden) — process env wins
+    over .env file contents.
+
+    Returns
+    -------
+    bool
+        True if the file was successfully read and parsed, False on error.
+    """
+    if not (path.exists() and path.is_file()):
+        return False
+    try:
+        parsed = parse_src_file(path)
+        for key, value in parsed.items():
+            # Only set if not already in environment (env takes precedence)
+            if key not in os.environ:
+                os.environ[key] = value
+        return True
+    except Exception:
+        return False
+
+
+
+[docs] +def load_dotenv( + dotenv_path: Optional[str] = None, + *, + walk_up: bool = False, + stop_at: Optional[Union[str, Path]] = None, +) -> bool: + """Load environment variables from .env file(s). + + Default behavior (``walk_up=False``, backward compatible): + Searches for .env file in the following order, loading the first match: + 1. Explicit ``dotenv_path`` if provided + 2. Current working directory (``cwd/.env``) + 3. User home directory (``$HOME/.env``) + + Parent-walking behavior (``walk_up=True``, opt-in): + Walks parent directories starting from ``cwd``, looking for ``.env`` + at each level. Stops when reaching ``stop_at`` (or ``$HOME`` if not + given) or the filesystem root. **All** ``.env`` files found are loaded, + with the most-distant parent loaded first so that closer-to-cwd values + take precedence (closer .env wins). An existing process env var is + never overridden by any .env (process env > closest .env > ... > root .env). + + Note: ``walk_up=True`` is ignored if ``dotenv_path`` is explicitly given. + + Parameters + ---------- + dotenv_path : str, optional + Path to .env file. If None, searches default locations. + walk_up : bool, optional + If True (and ``dotenv_path`` not given), walk parent dirs from cwd. + Default False for backward compatibility — new callers should pass True. + stop_at : str or Path, optional + Directory at which to stop the upward walk (inclusive — its ``.env`` + is considered). If None, stops at ``$HOME`` (or filesystem root if + ``$HOME`` is not a parent of cwd). Only used when ``walk_up=True``. + + Returns + ------- + bool + True if at least one .env file was found and loaded, False otherwise. + """ + if dotenv_path: + path = Path(dotenv_path) + if path.exists() and path.is_file(): + return _parse_dotenv_file(path) + return False + + if not walk_up: + # Legacy behavior: try cwd then $HOME, first hit wins. + for path in (Path.cwd() / ".env", Path.home() / ".env"): + if path.exists() and path.is_file(): + return _parse_dotenv_file(path) + return False + + # walk_up=True: collect .env files from cwd up through parents. + cwd = Path.cwd().resolve() + home = Path.home().resolve() + stop_dir = Path(stop_at).expanduser().resolve() if stop_at is not None else home + + collected: List[Path] = [] + current = cwd + visited: set = set() + while True: + resolved = current.resolve() + if resolved in visited: + break + visited.add(resolved) + + candidate = current / ".env" + if candidate.exists() and candidate.is_file(): + collected.append(candidate) + + # Stop condition: reached the configured stop directory. + if resolved == stop_dir: + break + + parent = current.parent + if parent == current: + # Filesystem root reached. + break + current = parent + + if not collected: + return False + + # `collected` is in cwd→root order (closest first). Load in that order: + # `_parse_dotenv_file` skips keys already in os.environ, so the closest + # .env wins for any shared key. Process env (set before this call) wins + # over all .env files. + loaded_any = False + for path in collected: + if _parse_dotenv_file(path): + loaded_any = True + return loaded_any
+ + + +
+[docs] +def get_scitex_dir(direct_val: Optional[str] = None) -> Path: + """Get SCITEX_DIR with priority: direct → env → default. + + This is a convenience function for the most common use case. + + Parameters + ---------- + direct_val : str, optional + Direct value (highest precedence) + + Returns + ------- + Path + Resolved SCITEX_DIR path + """ + # Try to load .env first (won't override existing env vars) + load_dotenv() + + if direct_val is not None: + return Path(direct_val).expanduser() + + env_val = os.getenv("SCITEX_DIR") + if env_val: + return Path(env_val).expanduser() + + return Path.home() / ".scitex"
+ + + +
+[docs] +class PriorityConfig: + """Universal config resolver with precedence: direct → config_dict → env → default + + Config dict (from YAML or passed dict) takes priority over env variables. + This follows the Scholar module's CascadeConfig pattern. + + Examples + -------- + >>> from scitex_config import PriorityConfig + >>> config = PriorityConfig(config_dict={"port": 3000}, env_prefix="SCITEX_") + >>> port = config.resolve("port", None, default=8000, type=int) + 3000 # from config_dict (highest after direct) + >>> # With env: SCITEX_PORT=5000 python script.py + >>> port = config.resolve("port", None, default=8000, type=int) + 3000 # config_dict takes precedence over env + >>> port = config.resolve("port", 9000, default=8000, type=int) + 9000 # direct value takes highest precedence + """ + + SENSITIVE_EXPRESSIONS = [ + "API", + "PASSWORD", + "SECRET", + "TOKEN", + "KEY", + "PASS", + "AUTH", + "CREDENTIAL", + "PRIVATE", + "CERT", + ] + +
+[docs] + def __init__( + self, + config_dict: Optional[Dict[str, Any]] = None, + env_prefix: str = "", + auto_uppercase: bool = True, + ): + """Initialize PriorityConfig. + + Parameters + ---------- + config_dict : dict, optional + Dictionary with configuration values + env_prefix : str + Prefix for environment variables (e.g., "SCITEX_") + auto_uppercase : bool + Whether to uppercase keys for env lookup + """ + self.config_dict = config_dict or {} + self.env_prefix = env_prefix + self.auto_uppercase = auto_uppercase + self.resolution_log: List[Dict[str, Any]] = []
+ + + def __repr__(self) -> str: + return f"PriorityConfig(prefix='{self.env_prefix}', configs={len(self.config_dict)})" + +
+[docs] + def get(self, key: str) -> Any: + """Get value from config dict only.""" + return self.config_dict.get(key)
+ + +
+[docs] + def resolve( + self, + key: str, + direct_val: Any = None, + default: Any = None, + type: Type = str, + mask: Optional[bool] = None, + ) -> Any: + """Get value with precedence hierarchy. + + Precedence: direct → config_dict → env → default + + This follows the Scholar module's CascadeConfig pattern where + config dict takes higher priority than environment variables. + + Parameters + ---------- + key : str + Configuration key to resolve + direct_val : Any, optional + Direct value (highest precedence) + default : Any, optional + Default value if not found elsewhere + type : Type + Type conversion (str, int, float, bool, list) + mask : bool, optional + Override automatic masking of sensitive values + + Returns + ------- + Any + Resolved configuration value + """ + source = None + final_value = None + + # Replace dots with underscores for env key (e.g., axes.width_mm -> AXES_WIDTH_MM) + normalized_key = key.replace(".", "_") + env_key = f"{self.env_prefix}{normalized_key.upper() if self.auto_uppercase else normalized_key}" + env_val = os.getenv(env_key) + + # Priority: direct → config_dict → env → default + if direct_val is not None: + source = "direct" + final_value = direct_val + elif key in self.config_dict: + source = "config_dict" + final_value = self.config_dict[key] + elif env_val: + source = f"env:{env_key}" + final_value = self._convert_type(env_val, type) + else: + source = "default" + final_value = default + + if mask is False: + should_mask = False + else: + should_mask = self._is_sensitive(key) + + display_value = self._mask_value(final_value) if should_mask else final_value + + self.resolution_log.append( + { + "key": key, + "source": source, + "value": display_value, + "type": type.__name__, + } + ) + + return final_value
+ + +
+[docs] + def print_resolutions(self) -> None: + """Print how each config was resolved.""" + if not self.resolution_log: + print("No configurations resolved yet") + return + + print("Configuration Resolution Log:") + print("-" * 50) + for entry in self.resolution_log: + print(f"{entry['key']:<20} = {entry['value']:<20} ({entry['source']})")
+ + +
+[docs] + def clear_log(self) -> None: + """Clear resolution log.""" + self.resolution_log = []
+ + + def _convert_type(self, value: str, type: Type) -> Any: + """Convert string value to specified type.""" + if type == int: + return int(value) + elif type == float: + return float(value) + elif type == bool: + return value.lower() in ("true", "1", "yes") + elif type == list: + return value.split(",") + return value + + def _is_sensitive(self, key: str) -> bool: + """Check if key contains sensitive expressions.""" + key_upper = key.upper() + return any(expr in key_upper for expr in self.SENSITIVE_EXPRESSIONS) + + def _mask_value(self, value: Any) -> str: + """Mask sensitive values for display.""" + if value is None: + return None + value_str = str(value) + if len(value_str) <= 4: + return "****" + return value_str[:2] + "*" * (len(value_str) - 4) + value_str[-2:]
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_config/_ScitexConfig.html b/src/scitex/_sphinx_html/_modules/scitex_config/_ScitexConfig.html new file mode 100644 index 000000000..2b4b5f83a --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_config/_ScitexConfig.html @@ -0,0 +1,1028 @@ + + + + + + + + scitex_config._ScitexConfig — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_config._ScitexConfig

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Timestamp: "2025-12-09 (ywatanabe)"
+# File: /home/ywatanabe/proj/scitex-code/src/scitex/config/ScitexConfig.py
+
+"""
+YAML-based configuration for SciTeX with environment variable substitution.
+
+Functionalities
+---------------
+- `load_yaml()` — parse YAML with `${VAR:-default}` env substitution.
+- `ScitexConfig` — YAML-backed config object exposing `.resolve(key, default=...)`.
+- `get_config()` — module-level cached accessor returning the default `ScitexConfig`.
+
+IO
+--
+- Reads: YAML files (default `src/scitex_config/default.yaml` or user-supplied path),
+  `os.environ` for `${VAR}` expansion.
+- Writes: none (read-only resolver).
+
+Dependencies
+------------
+- stdlib (`os`, `re`, `pathlib`, `typing`).
+- Sibling module `._PriorityConfig` (`PriorityConfig`, `load_dotenv`).
+- External: `PyYAML`.
+
+Usage
+-----
+    from scitex_config import ScitexConfig
+
+    # Load default configuration
+    config = ScitexConfig()
+
+    # Load custom configuration
+    config = ScitexConfig(config_path="/path/to/config.yaml")
+
+    # Resolve values with precedence
+    log_level = config.resolve("logging.level", default="INFO")
+"""
+
+import os
+import re
+from pathlib import Path
+from typing import Any, Optional, Type, Union
+
+from ._PriorityConfig import PriorityConfig, load_dotenv
+
+
+
+[docs] +def load_yaml(path: Path) -> dict: + """Load YAML file with environment variable substitution. + + Supports ${VAR:-default} syntax for environment variable expansion. + + Parameters + ---------- + path : Path + Path to YAML file + + Returns + ------- + dict + Parsed YAML with environment variables substituted + """ + try: + import yaml + except ImportError: + raise ImportError( + "PyYAML required for YAML config. Install with: pip install pyyaml" + ) + + try: + with open(path) as f: + content = f.read() + + def env_replacer(match): + """Replace ${VAR:-default} with environment variable or default.""" + env_expr = match.group(1) + if ":-" in env_expr: + var_name, default_value = env_expr.split(":-", 1) + value = os.getenv(var_name, default_value.strip('"')) + else: + value = os.getenv(env_expr) + + # Handle special values + if value in ["true", "false"]: + return value + elif value == "null": + return "null" + elif value and not (value.startswith('"') and value.endswith('"')): + return f'"{value}"' + else: + return value or "null" + + content = re.sub(r"\$\{([^}]+)\}", env_replacer, content) + return yaml.safe_load(content) + except Exception as e: + raise ValueError(f"Failed to load YAML config from {path}: {e}")
+ + + +
+[docs] +class ScitexConfig: + """YAML-based configuration manager for SciTeX. + + Loads configuration from YAML files with environment variable substitution. + Values can be resolved with priority: direct → config → env → default. + + Examples + -------- + >>> from scitex.config import ScitexConfig + >>> config = ScitexConfig() + >>> config.resolve("logging.level", default="INFO") + 'INFO' + >>> config.get("debug.enabled") + False + """ + +
+[docs] + def __init__( + self, + config_path: Optional[Union[str, Path]] = None, + env_prefix: str = "SCITEX_", + ): + """Initialize ScitexConfig. + + Parameters + ---------- + config_path : str or Path, optional + Path to custom YAML config file. If None, uses default.yaml. + env_prefix : str + Prefix for environment variables (default: "SCITEX_") + """ + # Load .env file first + load_dotenv() + + # Load YAML configuration + if config_path and Path(config_path).exists(): + self._config_data = load_yaml(Path(config_path)) + self._config_path = Path(config_path) + else: + default_path = Path(__file__).parent / "default.yaml" + if default_path.exists(): + self._config_data = load_yaml(default_path) + else: + self._config_data = {} + self._config_path = default_path + + # Flatten nested config for easy access + self._flat_config = self._flatten_dict(self._config_data) + + # Initialize PriorityConfig for resolution + self._priority_config = PriorityConfig( + config_dict=self._flat_config, + env_prefix=env_prefix, + )
+ + + def _flatten_dict(self, d: dict, parent_key: str = "", sep: str = ".") -> dict: + """Flatten nested dictionary with dot notation keys. + + Parameters + ---------- + d : dict + Dictionary to flatten + parent_key : str + Parent key for recursion + sep : str + Separator for nested keys + + Returns + ------- + dict + Flattened dictionary + """ + items = [] + for k, v in d.items(): + new_key = f"{parent_key}{sep}{k}" if parent_key else k + if isinstance(v, dict): + items.extend(self._flatten_dict(v, new_key, sep).items()) + else: + items.append((new_key, v)) + return dict(items) + +
+[docs] + def get(self, key: str, default: Any = None) -> Any: + """Get value from config directly (no precedence resolution). + + Supports dot notation for nested keys. + + Parameters + ---------- + key : str + Configuration key (e.g., "logging.level" or "debug.enabled") + default : Any + Default value if key not found + + Returns + ------- + Any + Configuration value + """ + return self._flat_config.get(key, default)
+ + +
+[docs] + def resolve( + self, + key: str, + direct_val: Any = None, + default: Any = None, + type: Type = str, + ) -> Any: + """Resolve value with precedence: direct → config → env → default. + + This follows the Scholar module's CascadeConfig pattern where + YAML config takes higher priority than environment variables. + + Parameters + ---------- + key : str + Configuration key (e.g., "logging.level") + direct_val : Any + Direct value (highest precedence) + default : Any + Default value (lowest precedence) + type : Type + Type conversion (str, int, float, bool, list) + + Returns + ------- + Any + Resolved value + """ + # Priority: direct → config → env → default + # (Same as Scholar's CascadeConfig pattern) + if direct_val is not None: + return direct_val + + # Config (YAML) takes priority over env + config_val = self._flat_config.get(key) + if config_val is not None: + return config_val + + # Then check environment variable + normalized_key = key.replace(".", "_") + env_key = f"SCITEX_{normalized_key.upper()}" + env_val = os.getenv(env_key) + if env_val: + return self._convert_type(env_val, type) + + return default
+ + + def _convert_type(self, value: str, type: Type) -> Any: + """Convert string value to specified type.""" + if type == int: + return int(value) + elif type == float: + return float(value) + elif type == bool: + return value.lower() in ("true", "1", "yes") + elif type == list: + return value.split(",") + return value + +
+[docs] + def get_nested(self, *keys: str, default: Any = None) -> Any: + """Get nested value from original config structure. + + Parameters + ---------- + *keys : str + Keys to traverse (e.g., "browser", "screenshots_dir") + default : Any + Default value if not found + + Returns + ------- + Any + Nested value + """ + current = self._config_data + for key in keys: + if isinstance(current, dict) and key in current: + current = current[key] + else: + return default + return current
+ + + @property + def config_path(self) -> Path: + """Get the path to the loaded config file.""" + return self._config_path + + @property + def raw(self) -> dict: + """Get raw configuration data (original nested structure).""" + return self._config_data + + @property + def flat(self) -> dict: + """Get flattened configuration data.""" + return self._flat_config + +
+[docs] + def print(self) -> None: + """Print configuration resolution log.""" + self._priority_config.print_resolutions()
+ + + def __repr__(self) -> str: + return f"ScitexConfig(path='{self._config_path}')"
+ + + +# Module-level convenience functions + +_default_config: Optional[ScitexConfig] = None + + +
+[docs] +def get_config(config_path: Optional[Union[str, Path]] = None) -> ScitexConfig: + """Get ScitexConfig instance. + + Parameters + ---------- + config_path : str or Path, optional + Path to custom config. If None, returns cached default instance. + + Returns + ------- + ScitexConfig + Configuration instance + """ + global _default_config + + if config_path is not None: + return ScitexConfig(config_path) + + if _default_config is None: + _default_config = ScitexConfig() + + return _default_config
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_config/_env_loader.html b/src/scitex/_sphinx_html/_modules/scitex_config/_env_loader.html new file mode 100644 index 000000000..2232feb63 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_config/_env_loader.html @@ -0,0 +1,853 @@ + + + + + + + + scitex_config._env_loader — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_config._env_loader

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Timestamp: "2026-05-30 (ywatanabe)"
+# File: ./src/scitex_config/_env_loader.py
+
+"""
+Environment variable loader for bash-compatible ``.src``/``.env`` files.
+
+Functionalities
+---------------
+- ``parse_src_file()`` — parse a single bash-style file into a ``{name: value}`` dict.
+- ``load_env_from_path()`` — parse a file or every ``*.src`` in a directory.
+- ``load_scitex_env()`` — apply ``$SCITEX_ENV_SRC`` contents to ``os.environ``.
+
+These are the canonical bash-style line/value parsers for the package; the
+``.env`` loader in ``_PriorityConfig`` delegates its per-line parsing here so
+quoting and ``$VAR`` expansion behave identically across both surfaces.
+
+IO
+--
+- Reads: process env (for ``$VAR`` expansion), ``.src``/``.env`` files.
+- Writes: ``os.environ`` (``load_scitex_env`` only).
+
+Dependencies
+------------
+- stdlib only (``logging``, ``os``, ``re``, ``pathlib``, ``typing``).
+"""
+
+from __future__ import annotations
+
+import logging
+import os
+import re
+from pathlib import Path
+from typing import Dict, List
+
+logger = logging.getLogger(__name__)
+
+# Pattern to match: export VAR=value or VAR=value (with optional quotes)
+_ENV_PATTERN = re.compile(r"^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=(.*)$")
+
+
+def _parse_value(value: str) -> str:
+    """Parse a bash-style value, handling quotes and escapes."""
+    value = value.strip()
+
+    # Handle double-quoted strings
+    if value.startswith('"') and value.endswith('"'):
+        value = value[1:-1]
+        # Unescape common bash escapes
+        value = value.replace('\\"', '"')
+        value = value.replace("\\$", "$")
+        value = value.replace("\\\\", "\\")
+    # Handle single-quoted strings (no escaping in bash single quotes)
+    elif value.startswith("'") and value.endswith("'"):
+        value = value[1:-1]
+
+    # Expand $VAR and ${VAR} references
+    def expand_var(match):
+        var_name = match.group(1) or match.group(2)
+        return os.environ.get(var_name, "")
+
+    value = re.sub(r"\$\{([^}]+)\}|\$([A-Za-z_][A-Za-z0-9_]*)", expand_var, value)
+
+    return value
+
+
+
+[docs] +def parse_src_file(filepath: Path) -> Dict[str, str]: + """ + Parse a bash-compatible ``.src``/``.env`` file and extract env variables. + + Parameters + ---------- + filepath : Path + Path to the file. + + Returns + ------- + dict + Dictionary of variable names to values. + """ + env_vars: Dict[str, str] = {} + + try: + with open(filepath) as f: + for line in f: + line = line.strip() + + # Skip empty lines and comments + if not line or line.startswith("#"): + continue + + match = _ENV_PATTERN.match(line) + if match: + name, value = match.groups() + env_vars[name] = _parse_value(value) + + except Exception as e: + logger.warning(f"Failed to parse {filepath}: {e}") + + return env_vars
+ + + +
+[docs] +def load_env_from_path(path: str) -> Dict[str, str]: + """ + Load environment variables from a file or directory. + + Parameters + ---------- + path : str + Path to a ``.src`` file or directory containing ``*.src`` files. + + Returns + ------- + dict + All loaded environment variables. + """ + loaded: Dict[str, str] = {} + path_obj = Path(path).expanduser() + + if not path_obj.exists(): + logger.warning(f"SCITEX_ENV_SRC path does not exist: {path}") + return loaded + + files_to_load: List[Path] = [] + + if path_obj.is_dir(): + # Load all .src files in directory + files_to_load = sorted(path_obj.glob("*.src")) + elif path_obj.is_file(): + files_to_load = [path_obj] + else: + logger.warning(f"SCITEX_ENV_SRC is not a file or directory: {path}") + return loaded + + for src_file in files_to_load: + env_vars = parse_src_file(src_file) + if env_vars: + logger.info(f"Loaded {len(env_vars)} vars from {src_file.name}") + loaded.update(env_vars) + + return loaded
+ + + +
+[docs] +def load_scitex_env() -> int: + """ + Load environment variables from ``$SCITEX_ENV_SRC`` if set. + + This function should be called early in the MCP server startup. + + Returns + ------- + int + Number of environment variables loaded. + """ + env_src = os.environ.get("SCITEX_ENV_SRC") + + if not env_src: + return 0 + + loaded = load_env_from_path(env_src) + + # Apply to current process environment + for name, value in loaded.items(): + os.environ[name] = value + + if loaded: + logger.info(f"SCITEX_ENV_SRC: Loaded {len(loaded)} environment variables") + + return len(loaded)
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_config/_paths.html b/src/scitex/_sphinx_html/_modules/scitex_config/_paths.html new file mode 100644 index 000000000..0758e0500 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_config/_paths.html @@ -0,0 +1,1033 @@ + + + + + + + + scitex_config._paths — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_config._paths

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Timestamp: "2025-12-09 (ywatanabe)"
+# File: /home/ywatanabe/proj/scitex-code/src/scitex/config/paths.py
+
+"""
+Centralized path management for SciTeX.
+
+Functionalities
+---------------
+- `ScitexPaths` — central path manager rooted at `$SCITEX_DIR`
+  (browser, cache, capture, logs, sessions, ...).
+- Property accessors return canonical defaults; `resolve(name, direct_val=...)`
+  supports the `direct → env → default` cascade per directory.
+- `get_paths()` — module-level cached accessor.
+
+IO
+--
+- Reads: `$SCITEX_DIR` env var, `.env` files (via `load_dotenv`).
+- Writes: creates child directories under `$SCITEX_DIR` on first access
+  (`mkdir(parents=True, exist_ok=True)`).
+
+Dependencies
+------------
+- stdlib (`os`, `pathlib`, `typing`).
+- Sibling module `._PriorityConfig` (`get_scitex_dir`, `load_dotenv`).
+
+Usage
+-----
+    from scitex_config import ScitexPaths
+
+    paths = ScitexPaths()
+
+    # Method 1: Direct property access (uses default)
+    print(paths.logs)           # ~/.scitex/logs
+    print(paths.cache)          # ~/.scitex/cache
+
+    # Method 2: resolve() with direct value override (recommended for modules)
+    cache_dir = paths.resolve("cache", direct_val=user_provided_path)
+    # If user_provided_path is None -> uses default from SCITEX_DIR
+
+    # Thread-safe: pass explicit base_dir
+    paths = ScitexPaths(base_dir="/custom/path")
+"""
+
+from pathlib import Path
+from typing import Optional, Union
+
+from ._PriorityConfig import get_scitex_dir
+
+
+
+[docs] +class ScitexPaths: + """Centralized path manager for SciTeX directories. + + All paths are derived from SCITEX_DIR (default: ~/.scitex). + Priority: direct_val → SCITEX_DIR env → .env file → default + + Directory Structure: + $SCITEX_DIR/ + ├── browser/ # Browser profiles and data + │ ├── screenshots/ # Browser debugging screenshots + │ ├── sessions/ # Shared browser sessions + │ └── persistent/ # Persistent browser profiles + ├── cache/ # General cache + │ └── functions/ # Function cache (joblib) + ├── capture/ # Screen captures + ├── impact_factor_cache/ # Impact factor data cache + ├── logs/ # Log files + ├── openathens_cache/ # OpenAthens auth cache + ├── rng/ # Random number generator state + ├── scholar/ # Scholar module data + │ ├── cache/ # Scholar-specific cache + │ └── library/ # PDF library + ├── screenshots/ # General screenshots + ├── test_monitor/ # Test monitoring screenshots + └── writer/ # Writer module data + """ + +
+[docs] + def __init__(self, base_dir: Optional[str] = None): + """Initialize ScitexPaths. + + Parameters + ---------- + base_dir : str, optional + Explicit base directory. If None, uses SCITEX_DIR env var + or falls back to ~/.scitex. + """ + self._base_dir = get_scitex_dir(base_dir)
+ + + @property + def base(self) -> Path: + """Base SciTeX directory ($SCITEX_DIR or ~/.scitex).""" + return self._base_dir + + # ========== Core directories ========== + + @property + def logs(self) -> Path: + """Log files directory.""" + return self._base_dir / "logs" + + @property + def cache(self) -> Path: + """General cache directory.""" + return self._base_dir / "cache" + + @property + def capture(self) -> Path: + """Screen capture directory.""" + return self._base_dir / "capture" + + @property + def screenshots(self) -> Path: + """General screenshots directory.""" + return self._base_dir / "screenshots" + + @property + def rng(self) -> Path: + """Random number generator state directory.""" + return self._base_dir / "rng" + + # ========== Browser directories ========== + + @property + def browser(self) -> Path: + """Browser module base directory.""" + return self._base_dir / "browser" + + @property + def browser_screenshots(self) -> Path: + """Browser debugging screenshots.""" + return self.browser / "screenshots" + + @property + def browser_sessions(self) -> Path: + """Shared browser sessions.""" + return self.browser / "sessions" + + @property + def browser_persistent(self) -> Path: + """Persistent browser profiles.""" + return self.browser / "persistent" + + @property + def test_monitor(self) -> Path: + """Test monitoring screenshots directory.""" + return self._base_dir / "test_monitor" + + # ========== Cache directories ========== + + @property + def function_cache(self) -> Path: + """Function cache (joblib memory).""" + return self.cache / "functions" + + @property + def impact_factor_cache(self) -> Path: + """Impact factor data cache.""" + return self._base_dir / "impact_factor_cache" + + @property + def openathens_cache(self) -> Path: + """OpenAthens authentication cache.""" + return self._base_dir / "openathens_cache" + + # ========== Scholar directories ========== + + @property + def scholar(self) -> Path: + """Scholar module base directory.""" + return self._base_dir / "scholar" + + @property + def scholar_cache(self) -> Path: + """Scholar-specific cache directory.""" + return self.scholar / "cache" + + @property + def scholar_library(self) -> Path: + """Scholar PDF library directory.""" + return self.scholar / "library" + + # ========== Writer directories ========== + + @property + def writer(self) -> Path: + """Writer module directory.""" + return self._base_dir / "writer" + + # ========== Resolve method (recommended for modules) ========== + +
+[docs] + def resolve( + self, + path_name: str, + direct_val: Optional[Union[str, Path]] = None, + ) -> Path: + """Resolve a path with priority: direct_val → default from SCITEX_DIR. + + This is the recommended method for modules that accept optional path + parameters. It follows the same pattern as PriorityConfig.resolve(). + + Parameters + ---------- + path_name : str + Name of the path property (e.g., "cache", "logs", "scholar_library") + direct_val : str or Path, optional + Direct value (highest precedence). If None, uses default. + + Returns + ------- + Path + Resolved path + + Examples + -------- + >>> paths = ScitexPaths() + >>> # User didn't provide path -> use default + >>> cache_dir = paths.resolve("cache", None) + >>> # User provided custom path -> use it + >>> cache_dir = paths.resolve("cache", "/custom/cache") + + Usage in modules: + >>> class MyModule: + ... def __init__(self, cache_dir=None): + ... self.cache_dir = get_paths().resolve("cache", cache_dir) + """ + if direct_val is not None: + return Path(direct_val).expanduser() + + # Get the default path from property + if hasattr(self, path_name): + return getattr(self, path_name) + + raise ValueError( + f"Unknown path name: {path_name}. Available: {list(self.list_all().keys())}" + )
+ + + # ========== Utility methods ========== + +
+[docs] + def ensure_dir(self, path: Path) -> Path: + """Ensure directory exists, creating if necessary. + + Parameters + ---------- + path : Path + Directory path to ensure exists. + + Returns + ------- + Path + The same path, guaranteed to exist. + """ + path.mkdir(parents=True, exist_ok=True) + return path
+ + +
+[docs] + def ensure_all(self) -> None: + """Create all standard directories.""" + dirs = [ + self.logs, + self.cache, + self.function_cache, + self.capture, + self.screenshots, + self.rng, + self.browser, + self.browser_screenshots, + self.browser_sessions, + self.browser_persistent, + self.test_monitor, + self.impact_factor_cache, + self.openathens_cache, + self.scholar, + self.scholar_cache, + self.scholar_library, + self.writer, + ] + for d in dirs: + d.mkdir(parents=True, exist_ok=True)
+ + +
+[docs] + def list_all(self) -> dict: + """List all configured paths. + + Returns + ------- + dict + Dictionary of path names to Path objects. + """ + return { + "base": self.base, + "logs": self.logs, + "cache": self.cache, + "function_cache": self.function_cache, + "capture": self.capture, + "screenshots": self.screenshots, + "rng": self.rng, + "browser": self.browser, + "browser_screenshots": self.browser_screenshots, + "browser_sessions": self.browser_sessions, + "browser_persistent": self.browser_persistent, + "test_monitor": self.test_monitor, + "impact_factor_cache": self.impact_factor_cache, + "openathens_cache": self.openathens_cache, + "scholar": self.scholar, + "scholar_cache": self.scholar_cache, + "scholar_library": self.scholar_library, + "writer": self.writer, + }
+ + + def __repr__(self) -> str: + return f"ScitexPaths(base='{self._base_dir}')"
+ + + +# Singleton instance for convenience (uses default SCITEX_DIR) +_default_paths: Optional[ScitexPaths] = None + + +
+[docs] +def get_paths(base_dir: Optional[str] = None) -> ScitexPaths: + """Get ScitexPaths instance. + + Parameters + ---------- + base_dir : str, optional + Explicit base directory. If None, returns cached default instance. + + Returns + ------- + ScitexPaths + Path manager instance. + """ + global _default_paths + + if base_dir is not None: + return ScitexPaths(base_dir) + + if _default_paths is None: + _default_paths = ScitexPaths() + + return _default_paths
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_context/_detect_environment.html b/src/scitex/_sphinx_html/_modules/scitex_context/_detect_environment.html new file mode 100644 index 000000000..01aaca7bd --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_context/_detect_environment.html @@ -0,0 +1,841 @@ + + + + + + + + scitex_context._detect_environment — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for scitex_context._detect_environment

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Timestamp: "2025-07-04 11:20:00 (ywatanabe)"
+# File: ./src/scitex/gen/_detect_environment.py
+
+"""
+Enhanced environment detection for SciTeX.
+
+Provides better discrimination between:
+- Python scripts
+- IPython console
+- Jupyter notebooks
+- Interactive Python
+"""
+
+import os
+import sys
+from typing import Literal, Tuple
+
+__all__ = ["detect_environment", "get_output_directory"]
+
+EnvironmentType = Literal["script", "jupyter", "ipython", "interactive", "unknown"]
+
+
+
+[docs] +def detect_environment() -> EnvironmentType: + """ + Detect the current execution environment. + + Returns + ------- + EnvironmentType + One of: 'script', 'jupyter', 'ipython', 'interactive', 'unknown' + + Examples + -------- + >>> env = detect_environment() + >>> print(f"Running in: {env}") + Running in: script + """ + # Check for Jupyter notebook + if "ipykernel" in sys.modules: + try: + ip = get_ipython() + if ip and type(ip).__name__ == "ZMQInteractiveShell": + return "jupyter" + except NameError: + pass + + # Check for IPython console + try: + ip = get_ipython() + if ip and type(ip).__name__ == "TerminalInteractiveShell": + return "ipython" + except NameError: + pass + + # Check for regular Python script + if sys.argv and sys.argv[0] and sys.argv[0].endswith(".py"): + return "script" + + # Check for interactive Python + if hasattr(sys, "ps1"): + return "interactive" + + return "unknown"
+ + + +
+[docs] +def get_output_directory( + specified_path: str, env_type: EnvironmentType = None +) -> Tuple[str, bool]: + """ + Get the appropriate output directory based on environment. + + Parameters + ---------- + specified_path : str + The path specified by the user + env_type : EnvironmentType, optional + Override environment detection + + Returns + ------- + tuple[str, bool] + (output_directory, should_use_temp) + + Examples + -------- + >>> output_dir, use_temp = get_output_directory("data.csv") + >>> print(f"Save to: {output_dir}, Temp: {use_temp}") + Save to: ./script_out/data.csv, Temp: False + """ + import inspect + + if env_type is None: + env_type = detect_environment() + + # For absolute paths, use as-is + if specified_path.startswith("/"): + return specified_path, False + + # Get base directory based on environment + if env_type == "script": + # Use script location + try: + script_path = inspect.stack()[1].filename + if script_path and not script_path.startswith("<"): + script_dir = os.path.dirname(os.path.abspath(script_path)) + script_name = os.path.splitext(os.path.basename(script_path))[0] + base_dir = os.path.join(script_dir, f"{script_name}_out") + return os.path.join(base_dir, specified_path), False + except: + pass + + elif env_type == "jupyter": + # For Jupyter, use current working directory with subdirectory + # This keeps outputs near the notebook + base_dir = os.path.join(os.getcwd(), "notebook_outputs") + return os.path.join(base_dir, specified_path), False + + elif env_type in ["ipython", "interactive"]: + # Use temp directory for console sessions + user = os.getenv("USER", "unknown") + base_dir = f"/tmp/{user}/{env_type}" + return os.path.join(base_dir, specified_path), True + + # Fallback: use current directory + return os.path.join("./output", specified_path), False
+ + + +
+[docs] +def is_notebook() -> bool: + """Check if running in Jupyter notebook.""" + return detect_environment() == "jupyter"
+ + + +
+[docs] +def is_ipython() -> bool: + """Check if running in IPython (console or notebook).""" + return detect_environment() in ["jupyter", "ipython"]
+ + + +
+[docs] +def is_script() -> bool: + """Check if running as a script.""" + return detect_environment() == "script"
+ + + +# Backward compatibility +def is_ipython_legacy() -> bool: + """Legacy IPython detection for compatibility.""" + try: + __IPYTHON__ + return True + except NameError: + return False + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_context/_get_notebook_path.html b/src/scitex/_sphinx_html/_modules/scitex_context/_get_notebook_path.html new file mode 100644 index 000000000..1da6d7b5f --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_context/_get_notebook_path.html @@ -0,0 +1,959 @@ + + + + + + + + scitex_context._get_notebook_path — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for scitex_context._get_notebook_path

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Timestamp: "2025-07-04 12:00:00 (ywatanabe)"
+# File: ./src/scitex/gen/_get_notebook_path.py
+
+"""
+Get the current Jupyter notebook path.
+
+This module provides functions to detect and retrieve the path
+of the currently running Jupyter notebook.
+"""
+
+import json
+import os
+from typing import Optional
+
+__all__ = ["get_notebook_path", "get_notebook_name"]
+
+
+
+[docs] +def get_notebook_path() -> Optional[str]: + """ + Get the full path of the current Jupyter notebook. + + Returns + ------- + Optional[str] + Full path to the notebook file, or None if not in a notebook + + Examples + -------- + >>> path = get_notebook_path() + >>> if path: + ... print(f"Running in notebook: {path}") + """ + try: + # Try to get from IPython + from IPython import get_ipython + + ip = get_ipython() + + if not ip: + return None + + # Check if we're in a notebook + if type(ip).__name__ != "ZMQInteractiveShell": + return None + + # Try multiple methods to get the notebook path + + # Method 1: From IPython's config + if hasattr(ip, "config") and hasattr(ip.config, "IPKernelApp"): + connection_file = ip.config.IPKernelApp.connection_file + if connection_file: + # Extract kernel ID from connection file + kernel_id = ( + os.path.basename(connection_file) + .split("-", 1)[1] + .replace(".json", "") + ) + + # Try to get notebook path from Jupyter server + notebook_path = _get_notebook_from_server(kernel_id) + if notebook_path: + return os.path.abspath(notebook_path) + + # Method 2: From environment variable (JupyterLab sets this) + if "JPY_SESSION_NAME" in os.environ: + session_name = os.environ["JPY_SESSION_NAME"] + if session_name.endswith(".ipynb"): + return os.path.abspath(session_name) + + # Method 3: From notebook's metadata + try: + # This works in some notebook environments + import ipykernel + import notebook + from notebook.notebookapp import list_running_servers + + for server in list_running_servers(): + response = _get_notebook_list(server["url"], server.get("token", "")) + if response: + for session in response: + if session["kernel"]["id"] == kernel_id: + return os.path.join( + server["notebook_dir"], session["notebook"]["path"] + ) + except: + pass + + except Exception: + pass + + return None
+ + + +def _get_notebook_from_server(kernel_id: str) -> Optional[str]: + """Try to get notebook path from Jupyter server API.""" + try: + import requests + from notebook.notebookapp import list_running_servers + + for server in list_running_servers(): + url = f"{server['url']}api/sessions" + token = server.get("token", "") + + if token: + headers = {"Authorization": f"token {token}"} + response = requests.get(url, headers=headers) + else: + response = requests.get(url) + + if response.status_code == 200: + sessions = response.json() + for session in sessions: + if session["kernel"]["id"] == kernel_id: + return session["notebook"]["path"] + except: + pass + + return None + + +def _get_notebook_list(server_url: str, token: str) -> Optional[list]: + """Get list of running notebooks from server.""" + try: + import requests + + url = f"{server_url}api/sessions" + + if token: + headers = {"Authorization": f"token {token}"} + response = requests.get(url, headers=headers) + else: + response = requests.get(url) + + if response.status_code == 200: + return response.json() + except: + pass + + return None + + +
+[docs] +def get_notebook_name() -> Optional[str]: + """ + Get just the name of the current notebook (without path). + + Returns + ------- + Optional[str] + Notebook filename, or None if not in a notebook + + Examples + -------- + >>> name = get_notebook_name() + >>> if name: + ... print(f"Notebook: {name}") + """ + notebook_path = get_notebook_path() + if notebook_path: + return os.path.basename(notebook_path) + return None
+ + + +
+[docs] +def get_notebook_directory() -> Optional[str]: + """ + Get the directory containing the current notebook. + + Returns + ------- + Optional[str] + Directory path, or None if not in a notebook + """ + notebook_path = get_notebook_path() + if notebook_path: + return os.path.dirname(os.path.abspath(notebook_path)) + return None
+ + + +# Simpler fallback method for when advanced detection fails +
+[docs] +def get_notebook_info_simple() -> tuple[Optional[str], Optional[str]]: + """ + Simple method to get notebook info using current working directory. + + Returns + ------- + tuple[Optional[str], Optional[str]] + (notebook_name, notebook_directory) + """ + try: + # In Jupyter, __file__ is not defined but we can use inspect + import inspect + import re + + # Get the call stack and look for notebook indicators + for frame_info in inspect.stack(): + filename = frame_info.filename + if filename: + # Check for ipynb in the filename + if filename.endswith(".ipynb"): + return os.path.basename(filename), os.path.dirname( + os.path.abspath(filename) + ) + + # Look for notebook execution patterns in the filename + # Jupyter often uses patterns like <ipython-input-...> or kernel UUIDs + if "<ipython-input" in filename or "ipykernel_" in filename: + # We're definitely in a notebook, need to find which one + break + + # If we can't find it in stack, check if we're in a notebook environment + from IPython import get_ipython + + ip = get_ipython() + + if ip and type(ip).__name__ == "ZMQInteractiveShell": + # We're in a notebook but can't determine the name from stack + # Try multiple strategies to find the notebook name + + # Strategy 1: Check IPython's config for hints + if hasattr(ip, "config") and hasattr(ip.config, "IPKernelApp"): + if hasattr(ip.config.IPKernelApp, "connection_file"): + conn_file = ip.config.IPKernelApp.connection_file + # Sometimes the connection file path contains hints about the notebook + if conn_file and ".ipynb" in conn_file: + match = re.search(r"([^/]+)\.ipynb", conn_file) + if match: + return match.group(0), os.getcwd() + + # Strategy 2: Check environment variables + for env_var in [ + "JUPYTER_NOTEBOOK_NAME", + "JPY_SESSION_NAME", + "NOTEBOOK_NAME", + ]: + if env_var in os.environ: + notebook_name = os.environ[env_var] + if notebook_name.endswith(".ipynb"): + return notebook_name, os.getcwd() + + # Strategy 3: Look for recently modified .ipynb files in current directory + cwd = os.getcwd() + try: + # Get all .ipynb files with their modification times + notebook_files = [] + for file in os.listdir(cwd): + if ( + file.endswith(".ipynb") + and not file.startswith(".") + and not file.endswith("_test_output.ipynb") + ): + file_path = os.path.join(cwd, file) + mtime = os.path.getmtime(file_path) + notebook_files.append((file, mtime)) + + # Sort by modification time (most recent first) + notebook_files.sort(key=lambda x: x[1], reverse=True) + + if notebook_files: + # Return the most recently modified notebook + # This is a good heuristic as the currently running notebook + # is likely to have been saved recently + return notebook_files[0][0], cwd + except: + pass + + # If no notebook found, return generic name + return "untitled.ipynb", cwd + + except: + pass + + return None, None
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_context/_suppress_output.html b/src/scitex/_sphinx_html/_modules/scitex_context/_suppress_output.html new file mode 100644 index 000000000..021294ca6 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_context/_suppress_output.html @@ -0,0 +1,714 @@ + + + + + + + + scitex_context._suppress_output — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for scitex_context._suppress_output

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Timestamp: "2025-10-13 08:18:37 (ywatanabe)"
+# File: /home/ywatanabe/proj/scitex_repo/src/scitex/context/_suppress_output.py
+# ----------------------------------------
+from __future__ import annotations
+
+import os
+
+__FILE__ = "./src/scitex/context/_suppress_output.py"
+__DIR__ = os.path.dirname(__FILE__)
+# ----------------------------------------
+
+from contextlib import contextmanager, redirect_stderr, redirect_stdout
+
+
+
+[docs] +@contextmanager +def suppress_output(suppress=True): + """ + A context manager that suppresses stdout and stderr. + + Example: + with suppress_output(): + print("This will not be printed to the console.") + """ + if suppress: + # Open a file descriptor that points to os.devnull (a black hole for data) + with open(os.devnull, "w") as fnull: + # Temporarily redirect stdout and stderr to the file descriptor fnull + with redirect_stdout(fnull), redirect_stderr(fnull): + # Yield control back to the context block + yield + else: + # If suppress is False, just yield without redirecting output + yield
+ + + +quiet = suppress_output + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_cv/_draw.html b/src/scitex/_sphinx_html/_modules/scitex_cv/_draw.html new file mode 100644 index 000000000..3ae7512b5 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_cv/_draw.html @@ -0,0 +1,925 @@ + + + + + + + + scitex_cv._draw — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_cv._draw

+#!/usr/bin/env python3
+# Timestamp: 2026-01-08
+# File: src/scitex/cv/_draw.py
+"""Drawing utilities using cv2."""
+
+from __future__ import annotations
+
+from typing import Tuple
+
+import cv2
+import numpy as np
+
+
+
+[docs] +def rectangle( + img: np.ndarray, + pt1: Tuple[int, int], + pt2: Tuple[int, int], + color: Tuple[int, int, int] = (0, 255, 0), + thickness: int = 2, + filled: bool = False, +) -> np.ndarray: + """Draw a rectangle on an image. + + Parameters + ---------- + img : np.ndarray + Input image (modified in place). + pt1 : tuple + Top-left corner (x, y). + pt2 : tuple + Bottom-right corner (x, y). + color : tuple + BGR color. + thickness : int + Line thickness (-1 for filled). + filled : bool + If True, fill the rectangle. + + Returns + ------- + np.ndarray + Image with rectangle. + """ + t = -1 if filled else thickness + cv2.rectangle(img, pt1, pt2, color, t) + return img
+ + + +
+[docs] +def circle( + img: np.ndarray, + center: Tuple[int, int], + radius: int, + color: Tuple[int, int, int] = (0, 255, 0), + thickness: int = 2, + filled: bool = False, +) -> np.ndarray: + """Draw a circle on an image. + + Parameters + ---------- + img : np.ndarray + Input image (modified in place). + center : tuple + Center coordinates (x, y). + radius : int + Circle radius. + color : tuple + BGR color. + thickness : int + Line thickness. + filled : bool + If True, fill the circle. + + Returns + ------- + np.ndarray + Image with circle. + """ + t = -1 if filled else thickness + cv2.circle(img, center, radius, color, t) + return img
+ + + +
+[docs] +def line( + img: np.ndarray, + pt1: Tuple[int, int], + pt2: Tuple[int, int], + color: Tuple[int, int, int] = (0, 255, 0), + thickness: int = 2, +) -> np.ndarray: + """Draw a line on an image. + + Parameters + ---------- + img : np.ndarray + Input image (modified in place). + pt1 : tuple + Start point (x, y). + pt2 : tuple + End point (x, y). + color : tuple + BGR color. + thickness : int + Line thickness. + + Returns + ------- + np.ndarray + Image with line. + """ + cv2.line(img, pt1, pt2, color, thickness) + return img
+ + + +
+[docs] +def text( + img: np.ndarray, + text: str, + position: Tuple[int, int], + color: Tuple[int, int, int] = (255, 255, 255), + scale: float = 1.0, + thickness: int = 2, + font: str = "simplex", +) -> np.ndarray: + """Draw text on an image. + + Parameters + ---------- + img : np.ndarray + Input image (modified in place). + text : str + Text to draw. + position : tuple + Bottom-left corner of text (x, y). + color : tuple + BGR color. + scale : float + Font scale. + thickness : int + Text thickness. + font : str + Font type: 'simplex', 'plain', 'duplex', 'complex', 'triplex'. + + Returns + ------- + np.ndarray + Image with text. + """ + font_map = { + "simplex": cv2.FONT_HERSHEY_SIMPLEX, + "plain": cv2.FONT_HERSHEY_PLAIN, + "duplex": cv2.FONT_HERSHEY_DUPLEX, + "complex": cv2.FONT_HERSHEY_COMPLEX, + "triplex": cv2.FONT_HERSHEY_TRIPLEX, + } + font_face = font_map.get(font, cv2.FONT_HERSHEY_SIMPLEX) + cv2.putText(img, text, position, font_face, scale, color, thickness) + return img
+ + + +
+[docs] +def polylines( + img: np.ndarray, + points: np.ndarray, + closed: bool = True, + color: Tuple[int, int, int] = (0, 255, 0), + thickness: int = 2, +) -> np.ndarray: + """Draw polylines on an image. + + Parameters + ---------- + img : np.ndarray + Input image (modified in place). + points : np.ndarray + Array of points with shape (N, 1, 2) or (N, 2). + closed : bool + Whether to close the polyline. + color : tuple + BGR color. + thickness : int + Line thickness. + + Returns + ------- + np.ndarray + Image with polylines. + """ + if len(points.shape) == 2: + points = points.reshape((-1, 1, 2)) + cv2.polylines(img, [points.astype(np.int32)], closed, color, thickness) + return img
+ + + +
+[docs] +def arrow( + img: np.ndarray, + pt1: Tuple[int, int], + pt2: Tuple[int, int], + color: Tuple[int, int, int] = (0, 255, 0), + thickness: int = 2, + tip_length: float = 0.1, +) -> np.ndarray: + """Draw an arrowed line on an image. + + Parameters + ---------- + img : np.ndarray + Input image (modified in place). + pt1 : tuple + Start point (x, y). + pt2 : tuple + End point (arrow tip) (x, y). + color : tuple + BGR color. + thickness : int + Line thickness. + tip_length : float + Arrow tip length as fraction of line length. + + Returns + ------- + np.ndarray + Image with arrow. + """ + cv2.arrowedLine(img, pt1, pt2, color, thickness, tipLength=tip_length) + return img
+ + + +__all__ = [ + "rectangle", + "circle", + "line", + "text", + "polylines", + "arrow", +] + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_cv/_filters.html b/src/scitex/_sphinx_html/_modules/scitex_cv/_filters.html new file mode 100644 index 000000000..14eb174e4 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_cv/_filters.html @@ -0,0 +1,903 @@ + + + + + + + + scitex_cv._filters — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_cv._filters

+#!/usr/bin/env python3
+# Timestamp: 2026-01-08
+# File: src/scitex/cv/_filters.py
+"""Image filtering utilities using cv2."""
+
+from __future__ import annotations
+
+import cv2
+import numpy as np
+
+
+
+[docs] +def blur( + img: np.ndarray, + ksize: int = 5, + method: str = "gaussian", +) -> np.ndarray: + """Apply blur to an image. + + Parameters + ---------- + img : np.ndarray + Input image. + ksize : int + Kernel size (must be odd). + method : str + Blur method: 'gaussian', 'median', 'box', 'bilateral'. + + Returns + ------- + np.ndarray + Blurred image. + """ + if ksize % 2 == 0: + ksize += 1 + + if method == "gaussian": + return cv2.GaussianBlur(img, (ksize, ksize), 0) + elif method == "median": + return cv2.medianBlur(img, ksize) + elif method == "box": + return cv2.blur(img, (ksize, ksize)) + elif method == "bilateral": + return cv2.bilateralFilter(img, ksize, 75, 75) + else: + raise ValueError(f"Unknown blur method: {method}")
+ + + +
+[docs] +def sharpen(img: np.ndarray, strength: float = 1.0) -> np.ndarray: + """Sharpen an image. + + Parameters + ---------- + img : np.ndarray + Input image. + strength : float + Sharpening strength. + + Returns + ------- + np.ndarray + Sharpened image. + """ + kernel = np.array( + [ + [0, -1, 0], + [-1, 5, -1], + [0, -1, 0], + ], + dtype=np.float32, + ) + + if strength != 1.0: + kernel = np.eye(3) + strength * (kernel - np.eye(3)) + + return cv2.filter2D(img, -1, kernel)
+ + + +
+[docs] +def edge_detect( + img: np.ndarray, + method: str = "canny", + low: int = 50, + high: int = 150, +) -> np.ndarray: + """Detect edges in an image. + + Parameters + ---------- + img : np.ndarray + Input image. + method : str + Edge detection method: 'canny', 'sobel', 'laplacian'. + low, high : int + Thresholds for Canny detector. + + Returns + ------- + np.ndarray + Edge image. + """ + if len(img.shape) == 3: + gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + else: + gray = img + + if method == "canny": + return cv2.Canny(gray, low, high) + elif method == "sobel": + sobel_x = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3) + sobel_y = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3) + return np.uint8(np.sqrt(sobel_x**2 + sobel_y**2)) + elif method == "laplacian": + return np.uint8(np.abs(cv2.Laplacian(gray, cv2.CV_64F))) + else: + raise ValueError(f"Unknown edge method: {method}")
+ + + +
+[docs] +def threshold( + img: np.ndarray, + thresh: int = 127, + maxval: int = 255, + method: str = "binary", +) -> np.ndarray: + """Apply thresholding to an image. + + Parameters + ---------- + img : np.ndarray + Input image. + thresh : int + Threshold value. + maxval : int + Maximum value. + method : str + Threshold method: 'binary', 'binary_inv', 'trunc', 'tozero', + 'tozero_inv', 'otsu', 'adaptive_mean', 'adaptive_gaussian'. + + Returns + ------- + np.ndarray + Thresholded image. + """ + if len(img.shape) == 3: + gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + else: + gray = img + + method_map = { + "binary": cv2.THRESH_BINARY, + "binary_inv": cv2.THRESH_BINARY_INV, + "trunc": cv2.THRESH_TRUNC, + "tozero": cv2.THRESH_TOZERO, + "tozero_inv": cv2.THRESH_TOZERO_INV, + } + + if method == "otsu": + _, result = cv2.threshold(gray, 0, maxval, cv2.THRESH_BINARY + cv2.THRESH_OTSU) + elif method == "adaptive_mean": + result = cv2.adaptiveThreshold( + gray, maxval, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2 + ) + elif method == "adaptive_gaussian": + result = cv2.adaptiveThreshold( + gray, + maxval, + cv2.ADAPTIVE_THRESH_GAUSSIAN_C, + cv2.THRESH_BINARY, + 11, + 2, + ) + else: + thresh_type = method_map.get(method, cv2.THRESH_BINARY) + _, result = cv2.threshold(gray, thresh, maxval, thresh_type) + + return result
+ + + +
+[docs] +def denoise( + img: np.ndarray, + strength: int = 10, + method: str = "fastNl", +) -> np.ndarray: + """Denoise an image. + + Parameters + ---------- + img : np.ndarray + Input image. + strength : int + Denoising strength. + method : str + Denoising method: 'fastNl', 'bilateral'. + + Returns + ------- + np.ndarray + Denoised image. + """ + if method == "fastNl": + if len(img.shape) == 3: + return cv2.fastNlMeansDenoisingColored(img, None, strength, strength) + else: + return cv2.fastNlMeansDenoising(img, None, strength) + elif method == "bilateral": + return cv2.bilateralFilter(img, 9, strength * 7.5, strength * 7.5) + else: + raise ValueError(f"Unknown denoise method: {method}")
+ + + +__all__ = [ + "blur", + "sharpen", + "edge_detect", + "threshold", + "denoise", +] + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_cv/_io.html b/src/scitex/_sphinx_html/_modules/scitex_cv/_io.html new file mode 100644 index 000000000..0e33b5bd3 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_cv/_io.html @@ -0,0 +1,843 @@ + + + + + + + + scitex_cv._io — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_cv._io

+#!/usr/bin/env python3
+# Timestamp: 2026-01-08
+# File: src/scitex/cv/_io.py
+"""Image I/O utilities using cv2."""
+
+from __future__ import annotations
+
+from pathlib import Path
+from typing import Union
+
+import cv2
+import numpy as np
+
+
+
+[docs] +def load( + path: Union[str, Path], + color: bool = True, + alpha: bool = False, +) -> np.ndarray: + """Load an image from file. + + Parameters + ---------- + path : str or Path + Image file path. + color : bool + If True, load as color (BGR). If False, load as grayscale. + alpha : bool + If True, preserve alpha channel (BGRA). + + Returns + ------- + np.ndarray + Image array in BGR or grayscale format. + """ + path = str(path) + if alpha: + img = cv2.imread(path, cv2.IMREAD_UNCHANGED) + elif color: + img = cv2.imread(path, cv2.IMREAD_COLOR) + else: + img = cv2.imread(path, cv2.IMREAD_GRAYSCALE) + + if img is None: + raise FileNotFoundError(f"Could not load image: {path}") + return img
+ + + +
+[docs] +def save( + img: np.ndarray, + path: Union[str, Path], + quality: int = 95, +) -> Path: + """Save an image to file. + + Parameters + ---------- + img : np.ndarray + Image array (BGR or grayscale). + path : str or Path + Output file path. + quality : int + JPEG quality (0-100) or PNG compression (0-9). + + Returns + ------- + Path + Path to saved file. + """ + path = Path(path) + path.parent.mkdir(parents=True, exist_ok=True) + + ext = path.suffix.lower() + if ext in [".jpg", ".jpeg"]: + params = [cv2.IMWRITE_JPEG_QUALITY, quality] + elif ext == ".png": + # PNG compression is 0-9, map quality to this + compression = max(0, min(9, (100 - quality) // 10)) + params = [cv2.IMWRITE_PNG_COMPRESSION, compression] + else: + params = [] + + success = cv2.imwrite(str(path), img, params) + if not success: + raise OSError(f"Failed to save image: {path}") + return path
+ + + +
+[docs] +def to_rgb(img: np.ndarray) -> np.ndarray: + """Convert BGR image to RGB. + + Parameters + ---------- + img : np.ndarray + BGR image. + + Returns + ------- + np.ndarray + RGB image. + """ + if len(img.shape) == 2: + return cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) + if img.shape[2] == 4: + return cv2.cvtColor(img, cv2.COLOR_BGRA2RGB) + return cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
+ + + +
+[docs] +def to_bgr(img: np.ndarray) -> np.ndarray: + """Convert RGB image to BGR. + + Parameters + ---------- + img : np.ndarray + RGB image. + + Returns + ------- + np.ndarray + BGR image. + """ + if len(img.shape) == 2: + return cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) + if img.shape[2] == 4: + return cv2.cvtColor(img, cv2.COLOR_RGBA2BGR) + return cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
+ + + +
+[docs] +def to_gray(img: np.ndarray) -> np.ndarray: + """Convert image to grayscale. + + Parameters + ---------- + img : np.ndarray + Color image (BGR or RGB). + + Returns + ------- + np.ndarray + Grayscale image. + """ + if len(img.shape) == 2: + return img + if img.shape[2] == 4: + return cv2.cvtColor(img, cv2.COLOR_BGRA2GRAY) + return cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
+ + + +__all__ = [ + "load", + "save", + "to_rgb", + "to_bgr", + "to_gray", +] + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_cv/_transform.html b/src/scitex/_sphinx_html/_modules/scitex_cv/_transform.html new file mode 100644 index 000000000..15ff426bc --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_cv/_transform.html @@ -0,0 +1,870 @@ + + + + + + + + scitex_cv._transform — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_cv._transform

+#!/usr/bin/env python3
+# Timestamp: 2026-01-08
+# File: src/scitex/cv/_transform.py
+"""Image transformation utilities using cv2."""
+
+from __future__ import annotations
+
+from typing import Optional, Tuple, Union
+
+import cv2
+import numpy as np
+
+
+
+[docs] +def resize( + img: np.ndarray, + size: Optional[Tuple[int, int]] = None, + scale: Optional[float] = None, + interpolation: str = "linear", +) -> np.ndarray: + """Resize an image. + + Parameters + ---------- + img : np.ndarray + Input image. + size : tuple of int, optional + Target size as (width, height). + scale : float, optional + Scale factor (alternative to size). + interpolation : str + Interpolation method: 'nearest', 'linear', 'cubic', 'area', 'lanczos'. + + Returns + ------- + np.ndarray + Resized image. + """ + interp_map = { + "nearest": cv2.INTER_NEAREST, + "linear": cv2.INTER_LINEAR, + "cubic": cv2.INTER_CUBIC, + "area": cv2.INTER_AREA, + "lanczos": cv2.INTER_LANCZOS4, + } + interp = interp_map.get(interpolation, cv2.INTER_LINEAR) + + if scale is not None: + return cv2.resize(img, None, fx=scale, fy=scale, interpolation=interp) + elif size is not None: + return cv2.resize(img, size, interpolation=interp) + else: + raise ValueError("Either size or scale must be provided")
+ + + +
+[docs] +def rotate( + img: np.ndarray, + angle: float, + center: Optional[Tuple[int, int]] = None, + scale: float = 1.0, +) -> np.ndarray: + """Rotate an image. + + Parameters + ---------- + img : np.ndarray + Input image. + angle : float + Rotation angle in degrees (counter-clockwise). + center : tuple of int, optional + Rotation center. Defaults to image center. + scale : float + Scale factor. + + Returns + ------- + np.ndarray + Rotated image. + """ + h, w = img.shape[:2] + if center is None: + center = (w // 2, h // 2) + + matrix = cv2.getRotationMatrix2D(center, angle, scale) + return cv2.warpAffine(img, matrix, (w, h))
+ + + +
+[docs] +def flip( + img: np.ndarray, + direction: str = "horizontal", +) -> np.ndarray: + """Flip an image. + + Parameters + ---------- + img : np.ndarray + Input image. + direction : str + Flip direction: 'horizontal', 'vertical', or 'both'. + + Returns + ------- + np.ndarray + Flipped image. + """ + flip_map = { + "horizontal": 1, + "vertical": 0, + "both": -1, + } + code = flip_map.get(direction, 1) + return cv2.flip(img, code)
+ + + +
+[docs] +def crop( + img: np.ndarray, + x: int, + y: int, + width: int, + height: int, +) -> np.ndarray: + """Crop a region from an image. + + Parameters + ---------- + img : np.ndarray + Input image. + x, y : int + Top-left corner coordinates. + width, height : int + Crop dimensions. + + Returns + ------- + np.ndarray + Cropped image. + """ + return img[y : y + height, x : x + width].copy()
+ + + +
+[docs] +def pad( + img: np.ndarray, + top: int = 0, + bottom: int = 0, + left: int = 0, + right: int = 0, + color: Union[int, Tuple[int, ...]] = 0, + mode: str = "constant", +) -> np.ndarray: + """Pad an image. + + Parameters + ---------- + img : np.ndarray + Input image. + top, bottom, left, right : int + Padding sizes. + color : int or tuple + Padding color for constant mode. + mode : str + Padding mode: 'constant', 'reflect', 'replicate'. + + Returns + ------- + np.ndarray + Padded image. + """ + mode_map = { + "constant": cv2.BORDER_CONSTANT, + "reflect": cv2.BORDER_REFLECT, + "replicate": cv2.BORDER_REPLICATE, + } + border_type = mode_map.get(mode, cv2.BORDER_CONSTANT) + return cv2.copyMakeBorder(img, top, bottom, left, right, border_type, value=color)
+ + + +__all__ = [ + "resize", + "rotate", + "flip", + "crop", + "pad", +] + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_dev/_cli/_utils.html b/src/scitex/_sphinx_html/_modules/scitex_dev/_cli/_utils.html new file mode 100644 index 000000000..090b3ebe3 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_dev/_cli/_utils.html @@ -0,0 +1,870 @@ + + + + + + + + scitex_dev._cli._utils — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_dev._cli._utils

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""CLI utilities for consuming Result objects."""
+
+from __future__ import annotations
+
+import json
+import sys
+from typing import Any, Callable
+
+from .._core.types import Result
+
+
+
+[docs] +def handle_result( + result: Result, + as_json: bool = False, + file: Any = None, +) -> int: + """Format and print a Result, return the exit code. + + Parameters + ---------- + result : Result + The structured result to display. + as_json : bool + If True, output full JSON. If False, human-friendly text. + file : file-like | None + Output stream. Defaults to stdout/stderr based on success. + + Returns + ------- + int + Exit code suitable for ``sys.exit()``. + """ + if as_json: + out = file or sys.stdout + print(result.to_json(), file=out) + elif result.success: + out = file or sys.stdout + data = result.data + if isinstance(data, (dict, list, tuple)): + print(json.dumps(data, indent=2, default=str), file=out) + else: + print(data, file=out) + else: + out = file or sys.stderr + print(f"Error: {result.error}", file=out) + if result.hints_on_error: + print("", file=out) + for hint in result.hints_on_error: + print(f" - {hint}", file=out) + + return result.exit_code
+ + + +
+[docs] +def run_as_cli( + fn: Callable, + as_json: bool = False, + **kwargs: Any, +) -> None: + """Call a ``@supports_return_as`` function and exit with proper code. + + Parameters + ---------- + fn : Callable + A function decorated with ``@supports_return_as``. + as_json : bool + If True, output full JSON. + **kwargs + Arguments to pass to ``fn``. + """ + result = fn(**kwargs, return_as="result") + code = handle_result(result, as_json=as_json) + sys.exit(code)
+ + + +
+[docs] +def wrap_as_cli( + fn: Callable, + as_json: bool = False, + **kwargs: Any, +) -> None: + """Call any function and display its result via CLI. + + Like ``wrap_as_mcp`` but for terminal output. Wraps any plain + function in Result, formats based on ``as_json``, and exits + with proper exit code. + + Parameters + ---------- + fn : Callable + Any callable returning data or raising exceptions. + as_json : bool + If True, output full JSON Result. If False, human-friendly text. + **kwargs + Arguments to pass to ``fn``. + """ + from .._core.errors import classify_exception + + try: + data = fn(**kwargs) + result = Result(success=True, data=data) + except Exception as exc: + error_code = classify_exception(exc) + hints_on_error = [] + suggestion = getattr(exc, "suggestion", None) + if suggestion: + hints_on_error.append(suggestion) + suggestions = getattr(exc, "suggestions", None) + if suggestions and isinstance(suggestions, list): + hints_on_error.extend(suggestions) + context = getattr(exc, "context", {}) + result = Result( + success=False, + error=str(exc), + error_code=error_code.value, + context=context if isinstance(context, dict) else {}, + hints_on_error=hints_on_error, + ) + + code = handle_result(result, as_json=as_json) + sys.exit(code)
+ + + +# --- Reusable CLI option factories --- + + +
+[docs] +def json_option(fn: Callable) -> Callable: + """Click decorator: adds ``--json`` flag as ``as_json`` parameter. + + Uses lazy ``import click`` so scitex-dev stays stdlib-only. + """ + import click + + return click.option( + "--json", + "as_json", + is_flag=True, + help="Output as structured JSON (Result envelope).", + )(fn)
+ + + +
+[docs] +def dry_run_option(fn: Callable) -> Callable: + """Click decorator: adds ``--dry-run`` flag. + + Uses lazy ``import click`` so scitex-dev stays stdlib-only. + """ + import click + + return click.option( + "--dry-run", + is_flag=True, + help="Preview changes without executing.", + )(fn)
+ + + +
+[docs] +def add_json_argument(parser: Any) -> None: + """Add ``--json`` flag to an argparse parser.""" + parser.add_argument( + "--json", + dest="as_json", + action="store_true", + default=False, + help="Output as structured JSON (Result envelope).", + )
+ + + +
+[docs] +def add_dry_run_argument(parser: Any) -> None: + """Add ``--dry-run`` flag to an argparse parser.""" + parser.add_argument( + "--dry-run", + dest="dry_run", + action="store_true", + default=False, + help="Preview changes without executing.", + )
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_dev/_core/decorators.html b/src/scitex/_sphinx_html/_modules/scitex_dev/_core/decorators.html new file mode 100644 index 000000000..8d1edefaf --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_dev/_core/decorators.html @@ -0,0 +1,746 @@ + + + + + + + + scitex_dev._core.decorators — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_dev._core.decorators

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""Decorator for opt-in structured Result wrapping.
+
+The ``@supports_return_as`` decorator adds a ``return_as`` keyword
+parameter to any function. By default the function behaves normally.
+With ``return_as="result"``, the return value is wrapped in a Result.
+"""
+
+from __future__ import annotations
+
+import functools
+from typing import Any, Callable
+
+from .errors import classify_exception
+from .types import Result
+
+
+
+[docs] +def supports_return_as(fn: Callable) -> Callable: + """Add ``return_as="result"`` support to a function. + + - ``return_as=None`` (default): data returned, exceptions raised. + - ``return_as="result"``: wrapped in ``Result``. + - Any other value: passed through to the original function. + + Examples + -------- + >>> @supports_return_as + ... def add(a: int, b: int) -> int: + ... return a + b + >>> add(1, 2) + 3 + >>> result = add(1, 2, return_as="result") + >>> result.success + True + >>> result.data + 3 + """ + + @functools.wraps(fn) + def wrapper(*args: Any, return_as: str | None = None, **kwargs: Any) -> Any: + if return_as != "result": + if return_as is not None: + kwargs["return_as"] = return_as + return fn(*args, **kwargs) + + try: + data = fn(*args, **kwargs) + return Result(success=True, data=data) + except Exception as exc: + error_code = classify_exception(exc) + suggestion = getattr(exc, "suggestion", None) + context = getattr(exc, "context", {}) + hints_on_error = [] + if suggestion: + hints_on_error.append(suggestion) + suggestions = getattr(exc, "suggestions", None) + if suggestions and isinstance(suggestions, list): + hints_on_error.extend(suggestions) + + return Result( + success=False, + error=str(exc), + error_code=error_code.value, + context=context if isinstance(context, dict) else {}, + hints_on_error=hints_on_error, + ) + + return wrapper
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_dev/_core/errors.html b/src/scitex/_sphinx_html/_modules/scitex_dev/_core/errors.html new file mode 100644 index 000000000..9dd1e3f47 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_dev/_core/errors.html @@ -0,0 +1,823 @@ + + + + + + + + scitex_dev._core.errors — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_dev._core.errors

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""Error code registry for LLM-friendly error categorization.
+
+Provides machine-readable error codes that map to CLI exit codes
+and guide LLM error recovery.
+"""
+
+from __future__ import annotations
+
+from enum import Enum
+
+
+
+[docs] +class ErrorCode(str, Enum): + """Machine-readable error codes for structured error responses.""" + + OK = "E000" + VALIDATION = "E001" + FILE_NOT_FOUND = "E002" + PERMISSION = "E003" + DEPENDENCY = "E004" + TIMEOUT = "E005" + RATE_LIMITED = "E006" + NETWORK = "E007" + CONFIG = "E008" + CONFLICT = "E009" + INTERNAL = "E999" + + @property + def exit_code(self) -> int: + """POSIX-style exit code for this error category.""" + return _EXIT_CODES.get(self, 1)
+ + + +_EXIT_CODES = { + ErrorCode.OK: 0, + ErrorCode.VALIDATION: 2, + ErrorCode.FILE_NOT_FOUND: 1, + ErrorCode.PERMISSION: 4, + ErrorCode.DEPENDENCY: 3, + ErrorCode.TIMEOUT: 5, + ErrorCode.RATE_LIMITED: 5, + ErrorCode.NETWORK: 1, + ErrorCode.CONFIG: 2, + ErrorCode.CONFLICT: 6, + ErrorCode.INTERNAL: 1, +} + +_BUILTIN_ERROR_MAP: dict[type, ErrorCode] = { + FileNotFoundError: ErrorCode.FILE_NOT_FOUND, + PermissionError: ErrorCode.PERMISSION, + TimeoutError: ErrorCode.TIMEOUT, + ConnectionError: ErrorCode.NETWORK, + ImportError: ErrorCode.DEPENDENCY, + ValueError: ErrorCode.VALIDATION, + TypeError: ErrorCode.VALIDATION, + KeyError: ErrorCode.CONFIG, +} + + +
+[docs] +class ScitexError(Exception): + """Canonical SciTeX exception for structured failures. + + Use when the failure crosses an MCP / CLI / HTTP boundary and the caller + (often an LLM agent) needs a machine-readable category plus a human + remediation hint. For plain Python errors raised inside a script, + prefer the standard library exceptions (``ValueError``, ``FileNotFoundError``, + etc.) — see ``general/03_interface/01_python-api/09_error-handling.md``. + + Parameters + ---------- + message : str + Human-readable description of the failure. + code : ErrorCode, optional + Machine-readable category. Defaults to ``ErrorCode.INTERNAL``. + remediation : str, optional + How to fix it (e.g. ``"pip install scitex-io[h5]"``). + + Examples + -------- + >>> raise ScitexError( + ... "h5py not installed", + ... code=ErrorCode.DEPENDENCY, + ... remediation="pip install scitex-io[h5]", + ... ) + """ + + def __init__( + self, + message: str, + *, + code: ErrorCode = ErrorCode.INTERNAL, + remediation: str | None = None, + ) -> None: + super().__init__(message) + self.message = message + self.error_code = code + self.remediation = remediation + +
+[docs] + def to_dict(self) -> dict[str, str]: + """Serialize to the structured shape consumed by MCP tools and `--json`.""" + out: dict[str, str] = { + "code": self.error_code.value, + "message": self.message, + } + if self.remediation: + out["remediation"] = self.remediation + return out
+ + + def __str__(self) -> str: + base = f"[{self.error_code.value}] {self.message}" + if self.remediation: + return f"{base} (remediation: {self.remediation})" + return base
+ + + +
+[docs] +def classify_exception(exc: Exception) -> ErrorCode: + """Classify an exception into an ErrorCode. + + Checks for a ``.error_code`` attribute first (SciTeXError convention), + then falls back to built-in exception type mapping. + """ + code = getattr(exc, "error_code", None) + if code is not None: + if isinstance(code, ErrorCode): + return code + if isinstance(code, str): + try: + return ErrorCode(code) + except ValueError: + pass + + for exc_type, error_code in _BUILTIN_ERROR_MAP.items(): + if isinstance(exc, exc_type): + return error_code + + return ErrorCode.INTERNAL
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_dev/_core/imports.html b/src/scitex/_sphinx_html/_modules/scitex_dev/_core/imports.html new file mode 100644 index 000000000..47e5b922b --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_dev/_core/imports.html @@ -0,0 +1,789 @@ + + + + + + + + scitex_dev._core.imports — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_dev._core.imports

+"""Optional-import helper for SciTeX standalone packages.
+
+Use in a `__init__.py` to replace inline `try/except ImportError` blocks:
+
+    from scitex_dev import try_import_optional
+
+    h5py = try_import_optional("h5py", extra="hdf5", pkg="scitex-io")
+    ndarray = try_import_optional("numpy", attr="ndarray")  # always installed
+    rel_mod = try_import_optional(".sub.thing", package="scitex_io")
+
+Returns the imported object on success and ``None`` on ``ImportError``.
+The original failure (module name + extra/pkg metadata) is recorded on the
+returned ``None`` is impossible — instead, callers can read the registry via
+``last_install_hint(name)`` to construct precise error messages at the
+use-site without re-raising.
+
+The `NotInstalled` sentinel proposal (see python-api skill TODO.md) is
+deferred — Pattern A (`X = None` always present in `__all__`) plus a
+companion `XXX_AVAILABLE = X is not None` flag covers all current use cases.
+"""
+
+from __future__ import annotations
+
+import importlib
+from dataclasses import dataclass
+from typing import Any
+
+__all__ = ["try_import_optional", "last_install_hint", "InstallHint"]
+
+
+
+[docs] +@dataclass(frozen=True) +class InstallHint: + """Metadata to help a caller surface a useful install message.""" + + module: str + extra: str | None + pkg: str | None + + def message(self) -> str: + if self.pkg and self.extra: + return ( + f"`{self.module}` is required. " + f"Install with: pip install '{self.pkg}[{self.extra}]'" + ) + if self.extra: + return ( + f"`{self.module}` is required. " + f"Re-install with the '{self.extra}' extra." + ) + return f"`{self.module}` is required. Install with: pip install {self.module}"
+ + + +_HINTS: dict[str, InstallHint] = {} + + +
+[docs] +def last_install_hint(name: str) -> InstallHint | None: + """Return the most recently recorded install hint for ``name`` (or ``None``).""" + return _HINTS.get(name)
+ + + +
+[docs] +def try_import_optional( + module_path: str, + attr: str | None = None, + *, + extra: str | None = None, + pkg: str | None = None, + package: str | None = None, +) -> Any: + """Import ``module_path``; return ``None`` on ``ImportError``. + + Parameters + ---------- + module_path : str + Module to import. Leading ``.`` triggers relative resolution against + ``package`` (mirrors :func:`importlib.import_module`). + attr : str, optional + If given, return ``getattr(module, attr)`` instead of the module. + A missing attribute is treated identically to a failed import. + extra : str, optional + Name of the pip extra that pulls this dependency (used for the + install hint surfaced via :func:`last_install_hint`). + pkg : str, optional + Distribution name owning the extra (e.g. ``"scitex-io"``). + package : str, optional + Anchor for relative imports. + + Returns + ------- + object or None + The imported module/attribute, or ``None`` on failure. + """ + if module_path.startswith(".") and package is None: + raise ValueError( + "relative imports require an explicit `package=` argument " + f"(got module_path={module_path!r})" + ) + try: + mod = importlib.import_module(module_path, package=package) + except ImportError: + _HINTS[module_path] = InstallHint(module=module_path, extra=extra, pkg=pkg) + return None + + if attr is None: + return mod + try: + return getattr(mod, attr) + except AttributeError: + _HINTS[module_path] = InstallHint(module=module_path, extra=extra, pkg=pkg) + return None
+ +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_dev/_core/side_effects.html b/src/scitex/_sphinx_html/_modules/scitex_dev/_core/side_effects.html new file mode 100644 index 000000000..0559b4f7a --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_dev/_core/side_effects.html @@ -0,0 +1,717 @@ + + + + + + + + scitex_dev._core.side_effects — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for scitex_dev._core.side_effects

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""Side effect declaration for mutation operations."""
+
+from __future__ import annotations
+
+from dataclasses import dataclass
+from typing import Literal
+
+SideEffectType = Literal[
+    "file_create",
+    "file_modify",
+    "file_delete",
+    "network",
+    "state_change",
+    "cache_write",
+]
+
+
+
+[docs] +@dataclass(frozen=True) +class SideEffect: + """A declared side effect of a function. + + Attributes + ---------- + type : str + Category (file_create, file_modify, file_delete, network, + state_change, cache_write). + target : str + What is affected (file path, URL, description). + undoable : bool + Whether the effect can be reversed. + """ + + type: SideEffectType + target: str + undoable: bool = False + + def __str__(self) -> str: + return f"{self.type}: {self.target}"
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_dev/_core/types.html b/src/scitex/_sphinx_html/_modules/scitex_dev/_core/types.html new file mode 100644 index 000000000..1cc569a7c --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_dev/_core/types.html @@ -0,0 +1,862 @@ + + + + + + + + scitex_dev._core.types — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_dev._core.types

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""Core Result type for LLM-friendly structured responses.
+
+The Result type wraps function return values with metadata needed by
+CLI and MCP consumers: success status, error codes, side effects,
+error hints, and idempotency flags.
+
+Python API users never see this type by default -- it is opt-in via
+the ``return_as="result"`` parameter added by ``@supports_return_as``.
+"""
+
+from __future__ import annotations
+
+import json
+from dataclasses import asdict, dataclass, field
+from typing import Generic, Optional, TypeVar
+
+T = TypeVar("T")
+
+
+def _normalize_hints(value) -> list[str]:
+    """Normalize hint values: None, "", and [] all become []."""
+    if value is None or value == "":
+        return []
+    if isinstance(value, str):
+        return [value] if value else []
+    if isinstance(value, list):
+        return [s for s in value if s]  # filter out None/"" items
+    return []
+
+
+
+[docs] +@dataclass +class Result(Generic[T]): + """Structured result wrapping function output with metadata. + + Attributes + ---------- + success : bool + Whether the operation succeeded. + data : T | None + The return value on success; None on failure. + error : str | None + Human-readable error message on failure. + error_code : str | None + Machine-readable error code (e.g. "E001"). + context : dict + Additional context (file paths, parameters, etc.). + side_effects : list[str] + Description of mutations performed. + hints_on_success : list[str] + Related tools the consumer might want (light, "see also" style). + Reserved for future use — currently empty. + hints_on_warning : list[str] + Hints for partial success, deprecation, or approaching limits. + Reserved for future use — currently empty. + hints_on_error : list[str] + Recovery guidance for expected failures (FAQ-style). + Accepts None, "", a single string, or a list. + idempotent : bool + Whether the operation is safe to retry. + version : str | None + API version for response schema evolution. + """ + + success: bool + data: Optional[T] = None + error: Optional[str] = None + error_code: Optional[str] = None + context: dict = field(default_factory=dict) + side_effects: list[str] = field(default_factory=list) + hints_on_success: list[str] = field(default_factory=list) + hints_on_warning: list[str] = field(default_factory=list) + hints_on_error: list[str] = field(default_factory=list) + idempotent: bool = False + version: Optional[str] = None + +
+[docs] + def __post_init__(self): + """Normalize hint fields (accept None, "", single string, or list).""" + self.side_effects = _normalize_hints(self.side_effects) + self.hints_on_success = _normalize_hints(self.hints_on_success) + self.hints_on_warning = _normalize_hints(self.hints_on_warning) + self.hints_on_error = _normalize_hints(self.hints_on_error)
+ + +
+[docs] + def to_dict(self) -> dict: + """Convert to a plain dictionary, stripping None values.""" + d = asdict(self) + return {k: v for k, v in d.items() if v is not None}
+ + +
+[docs] + def to_json(self, indent: int = 2) -> str: + """Serialize to a JSON string.""" + return json.dumps(self.to_dict(), indent=indent, default=str)
+ + + @property + def exit_code(self) -> int: + """Map error_code to a CLI exit code.""" + if self.success: + return 0 + return _error_code_to_exit(self.error_code) + +
+[docs] + @classmethod + def json_schema(cls) -> dict: + """Return JSON Schema describing the Result envelope.""" + return dict(RESULT_SCHEMA)
+
+ + + +RESULT_SCHEMA: dict = { + "type": "object", + "description": "Standardized Result envelope returned by all SciTeX MCP tools.", + "properties": { + "success": { + "type": "boolean", + "description": "Whether the operation succeeded.", + }, + "data": {"description": "Tool-specific return value (any JSON type)."}, + "error": { + "type": ["string", "null"], + "description": "Human-readable error message.", + }, + "error_code": { + "type": ["string", "null"], + "pattern": "^E\\d{3}$", + "description": "Machine-readable error code (E001-E999).", + }, + "context": { + "type": "object", + "description": "Additional context (file paths, params).", + }, + "side_effects": { + "type": "array", + "items": {"type": "string"}, + "description": "Mutations performed (file writes, network calls).", + }, + "hints_on_success": { + "type": "array", + "items": {"type": "string"}, + "description": "Related tools or actions (reserved, currently empty).", + }, + "hints_on_warning": { + "type": "array", + "items": {"type": "string"}, + "description": "Hints for partial success, deprecation (reserved, currently empty).", + }, + "hints_on_error": { + "type": "array", + "items": {"type": "string"}, + "description": "Recovery guidance for expected failures (FAQ-style).", + }, + "idempotent": {"type": "boolean", "description": "Safe to retry?"}, + "version": {"type": ["string", "null"], "description": "API version."}, + }, + "required": ["success"], +} + + +def _error_code_to_exit(error_code: str | None) -> int: + """Map an error code string to a CLI exit code.""" + _EXIT_MAP = { + "E000": 0, + "E001": 2, # validation + "E002": 1, # file not found + "E003": 4, # permission + "E004": 3, # dependency + "E005": 5, # timeout + "E006": 5, # rate limited + "E007": 1, # network + "E008": 2, # config + "E009": 6, # conflict + "E999": 1, # internal + } + if error_code is None: + return 1 + return _EXIT_MAP.get(error_code, 1) + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_dev/_docs/docs.html b/src/scitex/_sphinx_html/_modules/scitex_dev/_docs/docs.html new file mode 100644 index 000000000..93c4bcf2a --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_dev/_docs/docs.html @@ -0,0 +1,1108 @@ + + + + + + + + scitex_dev._docs.docs — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_dev._docs.docs

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""Public API for docs aggregation and retrieval across the SciTeX ecosystem.
+
+Usage:
+    from scitex_dev import get_docs, build_docs
+
+    # All installed packages (manifest overview)
+    get_docs()
+
+    # Single package — returns unwrapped result
+    get_docs(package="scitex-writer", format="json")
+
+    # Multiple packages — dict keyed by package name
+    get_docs(packages=["scitex-writer", "scitex-stats"], format="json")
+
+    # Specific page
+    get_docs(package="scitex-writer", format="html", page="api")
+
+    # Build docs from Sphinx source
+    build_docs(package="scitex-writer")
+    build_docs()  # all discovered packages
+
+    # Search across all packages
+    search_docs(query="save figure")
+    search_docs(query="statistics", packages=["scitex-stats"])
+"""
+
+from __future__ import annotations
+
+import json
+import logging
+from pathlib import Path
+from typing import Any, Optional
+
+from .._core.discovery import discover_packages, get_package_root, get_sphinx_source
+
+logger = logging.getLogger(__name__)
+
+
+
+[docs] +def get_docs( + package: Optional[str] = None, + packages: Optional[list[str]] = None, + format: Optional[str] = None, + page: Optional[str] = None, + *, + _discover_fn=None, + _root_fn=None, + _sphinx_fn=None, +) -> Any: + """Get documentation for one, several, or all installed SciTeX packages. + + Resolution chain per package: + 1. Pre-built _sphinx_html/ in installed package → fastest (production) + 2. Sphinx _build/ available → use existing build + 3. Neither → introspect from docstrings + signatures (always works) + + Args: + package: Single package name (returns unwrapped result). + packages: List of package names (returns dict keyed by name). + format: Output format — None for manifest, "json" for structured, + "html" for path to HTML directory. + page: Specific page name (only with format="html" or "json"). + + Returns: + - If package=: the doc result directly (unwrapped). + - If packages=: dict mapping package name → doc result. + - If neither: dict of all discovered packages → doc result. + + Raises: + ValueError: If both package= and packages= are given. + LookupError: If a requested package is not found. + """ + if package is not None and packages is not None: + raise ValueError( + "Use either package= (singular) or packages= (plural), not both" + ) + + discover = _discover_fn if _discover_fn is not None else discover_packages + + # Single package — unwrap + if package is not None: + return _get_one( + package, + format=format, + page=page, + _discover_fn=discover, + _root_fn=_root_fn, + _sphinx_fn=_sphinx_fn, + ) + + # Multiple or all packages + if packages is None: + discovered = discover() + packages = list(discovered.keys()) + + results = {} + for pkg in packages: + try: + results[pkg] = _get_one( + pkg, + format=format, + page=page, + _discover_fn=discover, + _root_fn=_root_fn, + _sphinx_fn=_sphinx_fn, + ) + except LookupError: + logger.warning("Package %s not found, skipping", pkg) + + return results
+ + + +
+[docs] +def build_docs( + package: Optional[str] = None, + output_dir: Optional[Path] = None, + formats: Optional[list[str]] = None, + *, + _discover_fn=None, + _sphinx_fn=None, +) -> dict[str, Any]: + """Build docs from Sphinx source for one or all packages. + + Args: + package: Single package name. None = build all discovered. + output_dir: Override output directory. Default: in-place (_build/). + formats: List of builders ("html", "json"). Default: ["html"]. + + Returns: + Dict mapping package name → build result (path or error). + + Raises: + LookupError: If package not found. + RuntimeError: If Sphinx is not installed. + """ + from .._core.builder import build_sphinx + + if formats is None: + formats = ["html"] + + discover = _discover_fn if _discover_fn is not None else discover_packages + sphinx = _sphinx_fn if _sphinx_fn is not None else get_sphinx_source + + discovered = discover() + + if package is not None: + targets = {package: discovered.get(package)} + if targets[package] is None: + raise LookupError( + f"Package '{package}' not found in scitex_dev.docs entry points. " + f"Available: {list(discovered.keys())}" + ) + else: + targets = discovered + + results = {} + for pkg_name, module_name in targets.items(): + if module_name is None: + continue + sphinx_src = sphinx(module_name) + if sphinx_src is None: + results[pkg_name] = {"error": "No Sphinx source found"} + continue + + pkg_results = {} + for fmt in formats: + try: + out = build_sphinx( + sphinx_src, + output_dir=(output_dir / fmt) if output_dir else None, + builder=fmt, + ) + pkg_results[fmt] = str(out) if out else None + except (RuntimeError, FileNotFoundError) as e: + pkg_results[fmt] = {"error": str(e)} + + results[pkg_name] = pkg_results + + return results
+ + + +
+[docs] +def search_docs( + query: str, + package: Optional[str] = None, + packages: Optional[list[str]] = None, + max_results: int = 10, + *, + _discover_fn=None, + _get_one_fn=None, +) -> list[dict[str, Any]]: + """Search documentation across one, several, or all SciTeX packages. + + Simple keyword search over page titles and introspected content. + Stdlib only — no external search dependencies. + + Args: + query: Search query string (case-insensitive keyword matching). + package: Search within a single package. + packages: Search within specific packages. + max_results: Maximum number of results to return. + + Returns: + List of dicts with keys: package, name, title, score, match_type. + Sorted by relevance score (descending). + """ + query_lower = query.lower() + query_terms = query_lower.split() + results = [] + + discover = _discover_fn if _discover_fn is not None else discover_packages + get_one = _get_one_fn if _get_one_fn is not None else _get_one + + # Determine which packages to search + if package is not None: + search_targets = {package: None} + elif packages is not None: + search_targets = {p: None for p in packages} + else: + search_targets = discover() + + for pkg_name in search_targets: + try: + manifest = get_one(pkg_name) + except LookupError: + continue + + if not isinstance(manifest, dict): + continue + + # Search page titles from manifest + for page_entry in manifest.get("pages", []): + if isinstance(page_entry, dict): + name = page_entry.get("name", "") + title = page_entry.get("title", "") + else: + name = str(page_entry) + title = name + + text = f"{name} {title}".lower() + score = sum(1 for term in query_terms if term in text) + if score > 0: + results.append( + { + "package": pkg_name, + "name": name, + "title": title, + "score": score, + "match_type": "page_title", + } + ) + + # Search introspected module/function names + for member_name, info in manifest.get("modules", {}).items(): + text = f"{member_name} {info.get('description', '')}".lower() + score = sum(1 for term in query_terms if term in text) + if score > 0: + results.append( + { + "package": pkg_name, + "name": member_name, + "title": info.get("description", "")[:80], + "score": score, + "match_type": "api_member", + } + ) + + # Search package description + desc = manifest.get("description", "").lower() + score = sum(1 for term in query_terms if term in desc) + if score > 0: + results.append( + { + "package": pkg_name, + "name": pkg_name, + "title": manifest.get("description", "")[:80], + "score": score, + "match_type": "package", + } + ) + + # Sort by score descending, then by name + results.sort(key=lambda r: (-r["score"], r["name"])) + return results[:max_results]
+ + + +# --------------------------------------------------------------------------- +# Internal helpers +# --------------------------------------------------------------------------- + + +def _get_one( + package: str, + format: Optional[str] = None, + page: Optional[str] = None, + *, + _discover_fn=None, + _root_fn=None, + _sphinx_fn=None, +) -> Any: + """Get docs for a single package, following the resolution chain.""" + discover = _discover_fn if _discover_fn is not None else discover_packages + root_lookup = _root_fn if _root_fn is not None else get_package_root + sphinx_lookup = _sphinx_fn if _sphinx_fn is not None else get_sphinx_source + + discovered = discover() + module_name = discovered.get(package) + + if module_name is None: + raise LookupError( + f"Package '{package}' not found. Available: {list(discovered.keys())}" + ) + + # 1. Try pre-built _sphinx_html/ + pkg_root = root_lookup(module_name) + if pkg_root is not None: + docs_dir = pkg_root / "_sphinx_html" + result = _resolve_from_built(docs_dir, format=format, page=page) + if result is not None: + return _enrich_manifest(result, package) + + # 2. Try Sphinx _build/ + sphinx_src = sphinx_lookup(module_name) + if sphinx_src is not None: + build_dir = sphinx_src / "_build" + result = _resolve_from_built( + build_dir / "html", format=format, page=page, json_dir=build_dir / "json" + ) + if result is not None: + return _enrich_manifest(result, package) + + # 3. Fallback: introspect + from .._core.introspect import introspect_package + + return introspect_package(module_name) + + +def _resolve_from_built( + html_dir: Path, + format: Optional[str] = None, + page: Optional[str] = None, + json_dir: Optional[Path] = None, +) -> Any: + """Try to resolve docs from a built docs directory. + + Returns None if the directory doesn't exist or doesn't have the requested content. + """ + if not html_dir.exists(): + return None + + # If page requested but no format, default to html + if page is not None and format is None: + format = "html" + + # No format requested → return manifest + if format is None: + from .._core.manifest import read_manifest, generate_manifest + + manifest = read_manifest(html_dir) + if manifest is not None: + return manifest + # Auto-generate manifest from directory contents + return generate_manifest( + package=html_dir.parent.name, + docs_dir=html_dir, + ) + + # HTML format → return path + if format == "html": + if page is not None: + page_path = html_dir / f"{page}.html" + if not page_path.exists(): + page_path = html_dir / page + if page_path.exists(): + return page_path + return None + if (html_dir / "index.html").exists(): + return html_dir + return None + + # JSON format → try Sphinx JSON builder output or read HTML + if format == "json": + target_dir = json_dir if json_dir and json_dir.exists() else html_dir + if page is not None: + json_path = target_dir / f"{page}.fjson" + if json_path.exists(): + with open(json_path, encoding="utf-8") as f: + return json.load(f) + # Try .json extension + json_path = target_dir / f"{page}.json" + if json_path.exists(): + with open(json_path, encoding="utf-8") as f: + return json.load(f) + return None + # Return list of available pages + pages = [] + for ext in ("*.fjson", "*.json"): + for p in sorted(target_dir.glob(ext)): + pages.append(p.stem) + if pages: + return {"pages": pages} + # Fall back to listing HTML pages + html_pages = [p.stem for p in sorted(html_dir.glob("*.html"))] + if html_pages: + return {"pages": html_pages, "note": "JSON not built, listing HTML pages"} + return None + + raise ValueError(f"Unknown format: {format!r}. Use None, 'html', or 'json'.") + + +def _enrich_manifest(result: Any, package: str) -> Any: + """Fill in version/description from importlib.metadata if missing.""" + if not isinstance(result, dict): + return result + if result.get("version") is not None and result.get("description") is not None: + return result + try: + from importlib.metadata import metadata + + meta = metadata(package) + if result.get("version") is None: + result["version"] = meta.get("Version") + if result.get("description") is None: + summary = meta.get("Summary") + if summary: + result["description"] = summary + except Exception: + pass + return result +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_dev/_docs/search.html b/src/scitex/_sphinx_html/_modules/scitex_dev/_docs/search.html new file mode 100644 index 000000000..fb4d102b8 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_dev/_docs/search.html @@ -0,0 +1,1145 @@ + + + + + + + + scitex_dev._docs.search — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_dev._docs.search

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""Unified search across the SciTeX ecosystem.
+
+Searches Python APIs, CLI commands, MCP tools, and documentation pages
+across all installed scitex packages.
+
+Query syntax (Google-like):
+    search("save figure")           # match any term
+    search('"save figure"')         # exact phrase match
+    search("stats -deprecated")     # exclude results with "deprecated"
+    search("+ttest statistics")     # "ttest" required, "statistics" optional boost
+
+Usage:
+    from scitex_dev import search
+
+    search("save figure")                      # search everything
+    search("ttest", scope="api")               # Python API only
+    search("docs", scope="cli")                # CLI subcommands only
+    search("stats -internal", scope="mcp")     # MCP tools, excluding "internal"
+"""
+
+from __future__ import annotations
+
+import difflib
+import logging
+import re
+from dataclasses import dataclass, field
+from typing import Any, Literal, Optional
+
+from .._core.discovery import discover_packages, get_package_root
+
+logger = logging.getLogger(__name__)
+
+Scope = Literal["all", "api", "cli", "mcp", "docs"]
+
+# Fuzzy match threshold (0.0–1.0). Lower = more permissive.
+_FUZZY_THRESHOLD = 0.6
+
+
+# ---------------------------------------------------------------------------
+# Query parsing
+# ---------------------------------------------------------------------------
+
+
+@dataclass
+class ParsedQuery:
+    """Parsed search query with Google-like operators."""
+
+    required: list[str] = field(default_factory=list)  # +term
+    optional: list[str] = field(default_factory=list)  # plain term
+    excluded: list[str] = field(default_factory=list)  # -term
+    phrases: list[str] = field(default_factory=list)  # "exact phrase"
+
+    @property
+    def all_positive(self) -> list[str]:
+        """All terms that should match (required + optional)."""
+        return self.required + self.optional
+
+    @property
+    def is_empty(self) -> bool:
+        return not (self.required or self.optional or self.phrases)
+
+
+def parse_query(query: str) -> ParsedQuery:
+    """Parse a Google-like query string.
+
+    Supports:
+        word         → optional term (boosts score)
+        +word        → required term (must match)
+        -word        → excluded term (must NOT match)
+        "phrase"     → exact phrase match
+    """
+    parsed = ParsedQuery()
+
+    # Extract quoted phrases first
+    for match in re.finditer(r'"([^"]+)"', query):
+        parsed.phrases.append(match.group(1).lower())
+
+    # Remove quoted parts from remaining query
+    remaining = re.sub(r'"[^"]*"', "", query).strip()
+
+    for token in remaining.split():
+        token_lower = token.lower()
+        if token.startswith("+") and len(token) > 1:
+            parsed.required.append(token_lower[1:])
+        elif token.startswith("-") and len(token) > 1:
+            parsed.excluded.append(token_lower[1:])
+        else:
+            parsed.optional.append(token_lower)
+
+    return parsed
+
+
+# ---------------------------------------------------------------------------
+# Scoring
+# ---------------------------------------------------------------------------
+
+
+def score_text(pq: ParsedQuery, text: str, fuzzy: bool = True) -> float:
+    """Score text against a parsed query.
+
+    Returns:
+        Score >= 0. Returns -1 if excluded term found or required term missing.
+    """
+    text_lower = text.lower()
+
+    # Check exclusions first
+    for term in pq.excluded:
+        if term in text_lower:
+            return -1
+
+    # Check required terms
+    for term in pq.required:
+        if not _term_matches(term, text_lower, fuzzy=fuzzy):
+            return -1
+
+    score = 0.0
+
+    # Score required terms (they matched, give credit)
+    for term in pq.required:
+        score += 2.0  # Required terms get double weight
+
+    # Score optional terms
+    for term in pq.optional:
+        if term in text_lower:
+            score += 1.0
+        elif fuzzy and _fuzzy_match(term, text_lower):
+            score += 0.5  # Fuzzy matches get half weight
+
+    # Score exact phrases
+    for phrase in pq.phrases:
+        if phrase in text_lower:
+            score += 3.0  # Phrase matches get triple weight
+
+    return score
+
+
+def _term_matches(term: str, text: str, fuzzy: bool = True) -> bool:
+    """Check if a term matches text (exact or fuzzy)."""
+    if term in text:
+        return True
+    if fuzzy:
+        return _fuzzy_match(term, text)
+    return False
+
+
+def _fuzzy_match(term: str, text: str) -> bool:
+    """Check if term fuzzy-matches any word in text."""
+    words = text.split()
+    for word in words:
+        # Strip non-alphanumeric for cleaner matching
+        clean = re.sub(r"[^a-z0-9_]", "", word)
+        if not clean:
+            continue
+        ratio = difflib.SequenceMatcher(None, term, clean).ratio()
+        if ratio >= _FUZZY_THRESHOLD:
+            return True
+    return False
+
+
+# ---------------------------------------------------------------------------
+# Main search function
+# ---------------------------------------------------------------------------
+
+
+
+
+
+
+# ---------------------------------------------------------------------------
+# Helpers
+# ---------------------------------------------------------------------------
+
+
+def _resolve_targets(
+    package: Optional[str],
+    packages: Optional[list[str]],
+) -> dict[str, Optional[str]]:
+    """Resolve which packages to search."""
+    if package is not None:
+        discovered = discover_packages()
+        return {package: discovered.get(package)}
+    if packages is not None:
+        discovered = discover_packages()
+        return {p: discovered.get(p) for p in packages}
+    return discover_packages()
+
+
+def _make_result(
+    package: str,
+    name: str,
+    title: str,
+    score: float,
+    scope: str,
+    match_type: str,
+) -> dict[str, Any]:
+    return {
+        "package": package,
+        "name": name,
+        "title": title,
+        "score": score,
+        "scope": scope,
+        "match_type": match_type,
+    }
+
+
+# ---------------------------------------------------------------------------
+# Scope-specific searchers
+# ---------------------------------------------------------------------------
+
+
+def _search_docs(
+    pq: ParsedQuery,
+    targets: dict[str, Optional[str]],
+    fuzzy: bool = True,
+) -> list[dict[str, Any]]:
+    """Search documentation pages."""
+    from .docs import get_docs
+
+    results = []
+    for pkg_name in targets:
+        try:
+            manifest = get_docs(package=pkg_name)
+        except LookupError:
+            continue
+        if not isinstance(manifest, dict):
+            continue
+
+        for page_entry in manifest.get("pages", []):
+            if isinstance(page_entry, dict):
+                name = page_entry.get("name", "")
+                title = page_entry.get("title", "")
+            else:
+                name = str(page_entry)
+                title = name
+
+            text = f"{name} {title}"
+            s = score_text(pq, text, fuzzy=fuzzy)
+            if s > 0:
+                results.append(
+                    _make_result(pkg_name, name, title, s, "docs", "page_title")
+                )
+
+        # Package description
+        desc = manifest.get("description", "")
+        if desc:
+            s = score_text(pq, desc, fuzzy=fuzzy)
+            if s > 0:
+                results.append(
+                    _make_result(pkg_name, pkg_name, desc[:80], s, "docs", "package")
+                )
+
+    return results
+
+
+def _search_api(
+    pq: ParsedQuery,
+    targets: dict[str, Optional[str]],
+    fuzzy: bool = True,
+) -> list[dict[str, Any]]:
+    """Search Python API (public functions, classes, methods)."""
+    from .._core.introspect import introspect_package
+
+    results = []
+    for pkg_name, module_name in targets.items():
+        if module_name is None:
+            continue
+        try:
+            info = introspect_package(module_name)
+        except Exception:
+            continue
+        if info is None:
+            continue
+
+        for member_name, member_info in info.get("modules", {}).items():
+            text = f"{member_name} {member_info.get('description', '')}"
+            sig = member_info.get("signature", "")
+            if sig:
+                text += f" {sig}"
+            s = score_text(pq, text, fuzzy=fuzzy)
+            if s > 0:
+                results.append(
+                    _make_result(
+                        pkg_name,
+                        member_name,
+                        member_info.get("description", "")[:80],
+                        s,
+                        "api",
+                        member_info.get("type", "function"),
+                    )
+                )
+
+            # Search methods within classes
+            if member_info.get("type") == "class":
+                for method_name, method_info in member_info.get("methods", {}).items():
+                    text = f"{member_name}.{method_name} {method_info.get('description', '')}"
+                    s = score_text(pq, text, fuzzy=fuzzy)
+                    if s > 0:
+                        results.append(
+                            _make_result(
+                                pkg_name,
+                                f"{member_name}.{method_name}",
+                                method_info.get("description", "")[:80],
+                                s,
+                                "api",
+                                "method",
+                            )
+                        )
+
+    return results
+
+
+def _search_cli(
+    pq: ParsedQuery,
+    targets: dict[str, Optional[str]],
+    fuzzy: bool = True,
+) -> list[dict[str, Any]]:
+    """Search CLI subcommands via console_scripts entry points."""
+    results = []
+    try:
+        from importlib.metadata import entry_points
+
+        eps = entry_points(group="console_scripts")
+        for ep in eps:
+            pkg_match = None
+            for pkg_name in targets:
+                normalized = pkg_name.replace("-", "_")
+                if normalized in ep.value or ep.name.startswith(pkg_name):
+                    pkg_match = pkg_name
+                    break
+
+            if pkg_match is None:
+                continue
+
+            text = f"{ep.name} {ep.value}"
+            s = score_text(pq, text, fuzzy=fuzzy)
+            if s > 0:
+                results.append(
+                    _make_result(
+                        pkg_match,
+                        ep.name,
+                        f"CLI: {ep.value}",
+                        s,
+                        "cli",
+                        "console_script",
+                    )
+                )
+    except Exception:
+        logger.debug("Failed to search CLI entry points")
+
+    return results
+
+
+def _search_mcp(
+    pq: ParsedQuery,
+    targets: dict[str, Optional[str]],
+    fuzzy: bool = True,
+) -> list[dict[str, Any]]:
+    """Search MCP tool names and descriptions."""
+    results = []
+
+    for pkg_name, module_name in targets.items():
+        if module_name is None:
+            continue
+
+        pkg_root = get_package_root(module_name)
+        if pkg_root is None:
+            continue
+
+        mcp_candidates = [
+            pkg_root / "_mcp_tools",
+            pkg_root / "mcp",
+            pkg_root / "_mcp",
+        ]
+
+        for mcp_dir in mcp_candidates:
+            if not mcp_dir.exists():
+                continue
+            for py_file in mcp_dir.glob("*.py"):
+                if py_file.name.startswith("_"):
+                    continue
+                try:
+                    content = py_file.read_text(encoding="utf-8")
+                except OSError:
+                    continue
+
+                for match in re.finditer(
+                    r'(?:async\s+)?def\s+(\w+)\s*\([^)]*\)\s*(?:->.*?)?:\s*\n\s*"""([^"]*)',
+                    content,
+                ):
+                    func_name = match.group(1)
+                    docstring = match.group(2).strip()
+                    text = f"{func_name} {docstring}"
+                    s = score_text(pq, text, fuzzy=fuzzy)
+                    if s > 0:
+                        results.append(
+                            _make_result(
+                                pkg_name,
+                                func_name,
+                                docstring[:80],
+                                s,
+                                "mcp",
+                                "mcp_tool",
+                            )
+                        )
+
+    return results
+
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_dev/_ecosystem/_mcp/_utils.html b/src/scitex/_sphinx_html/_modules/scitex_dev/_ecosystem/_mcp/_utils.html new file mode 100644 index 000000000..35a19c9f9 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_dev/_ecosystem/_mcp/_utils.html @@ -0,0 +1,838 @@ + + + + + + + + scitex_dev._ecosystem._mcp._utils — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for scitex_dev._ecosystem._mcp._utils

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""MCP utilities for consuming Result objects."""
+
+from __future__ import annotations
+
+from typing import Any, Callable
+
+from ..._core.types import Result
+
+
+
+[docs] +def run_as_mcp(fn: Callable, **kwargs: Any) -> str: + """Call a ``@supports_return_as`` function and return MCP-ready JSON. + + Parameters + ---------- + fn : Callable + A function decorated with ``@supports_return_as``. + **kwargs + Arguments to pass to ``fn``. + + Returns + ------- + str + JSON string with the full Result structure. + """ + result = fn(**kwargs, return_as="result") + return result.to_json()
+ + + +
+[docs] +def wrap_as_mcp( + fn: Callable, + *, + side_effects: list[str] | None = None, + hints_on_error: list[str] | None = None, + idempotent: bool = False, + **kwargs: Any, +) -> str: + """Call any function and wrap its return in Result JSON. + + Unlike ``run_as_mcp`` (which requires ``@supports_return_as``), + this wraps any plain function. Use this to retrofit existing + handlers without modifying the underlying function. + + Parameters + ---------- + fn : Callable + Any callable returning data or raising exceptions. + side_effects : list[str], optional + Declared side effects (e.g. ``["file_create: /tmp/out.csv"]``). + hints_on_error : list[str], optional + Recovery guidance for expected failures (FAQ-style). + idempotent : bool + Whether the operation is safe to retry. + **kwargs + Arguments to pass to ``fn``. + + Returns + ------- + str + JSON string with Result structure. + """ + from ..._core.errors import classify_exception + + try: + data = fn(**kwargs) + return Result( + success=True, + data=data, + side_effects=side_effects, + idempotent=idempotent, + ).to_json() + except Exception as exc: + error_code = classify_exception(exc) + err_hints = list(hints_on_error or []) + suggestion = getattr(exc, "suggestion", None) + if suggestion: + err_hints.append(suggestion) + suggestions = getattr(exc, "suggestions", None) + if suggestions and isinstance(suggestions, list): + err_hints.extend(suggestions) + context = getattr(exc, "context", {}) + return Result( + success=False, + error=str(exc), + error_code=error_code.value, + context=context if isinstance(context, dict) else {}, + hints_on_error=err_hints, + ).to_json()
+ + + +
+[docs] +async def async_wrap_as_mcp( + coro_fn: Callable, + *, + side_effects: list[str] | None = None, + hints_on_error: list[str] | None = None, + idempotent: bool = False, + **kwargs: Any, +) -> str: + """Async version of ``wrap_as_mcp`` for async handlers. + + Parameters + ---------- + coro_fn : Callable + Any async callable (coroutine function) returning data + or raising exceptions. + side_effects : list[str], optional + Declared side effects. + hints_on_error : list[str], optional + Recovery guidance for expected failures (FAQ-style). + idempotent : bool + Whether the operation is safe to retry. + **kwargs + Arguments to pass to ``coro_fn``. + + Returns + ------- + str + JSON string with Result structure. + """ + from ..._core.errors import classify_exception + + try: + data = await coro_fn(**kwargs) + return Result( + success=True, + data=data, + side_effects=side_effects, + idempotent=idempotent, + ).to_json() + except Exception as exc: + error_code = classify_exception(exc) + err_hints = list(hints_on_error or []) + suggestion = getattr(exc, "suggestion", None) + if suggestion: + err_hints.append(suggestion) + suggestions = getattr(exc, "suggestions", None) + if suggestions and isinstance(suggestions, list): + err_hints.extend(suggestions) + context = getattr(exc, "context", {}) + return Result( + success=False, + error=str(exc), + error_code=error_code.value, + context=context if isinstance(context, dict) else {}, + hints_on_error=err_hints, + ).to_json()
+ + + +
+[docs] +def result_to_mcp(result: Result) -> str: + """Convert an existing Result to MCP-ready JSON.""" + return result.to_json()
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_dev/_release/fix.html b/src/scitex/_sphinx_html/_modules/scitex_dev/_release/fix.html new file mode 100644 index 000000000..9c305d240 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_dev/_release/fix.html @@ -0,0 +1,1167 @@ + + + + + + + + scitex_dev._release.fix — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_dev._release.fix

+#!/usr/bin/env python3
+# Timestamp: 2026-03-27
+# File: scitex_dev/fix.py
+
+"""Detect and fix version mismatches across the ecosystem.
+
+Single-responsibility functions:
+- detect_mismatches: find packages with version inconsistencies
+- fix_local: pip install -e for local mismatches
+- fix_remote: git pull + pip install on remote hosts
+- fix_init_version: sync __init__.py with pyproject.toml
+- verify_versions: confirm all versions are aligned after fixes
+
+The combined fix_mismatches is kept for backward compatibility.
+
+Safety model: all mutating functions default to confirm=False (dry run).
+"""
+
+from __future__ import annotations
+
+from pathlib import Path
+from typing import Any
+
+from .._core.config import DevConfig, load_config
+from .._sync import sync_all, sync_local
+from .versions import get_mismatches
+
+
+# --- Single-responsibility functions ---
+
+
+def detect_mismatches(
+    packages: list[str] | None = None,
+) -> dict[str, Any]:
+    """Detect version mismatches. Read-only, no side effects.
+
+    Parameters
+    ----------
+    packages : list[str] | None
+        Package names. None = all ecosystem packages.
+
+    Returns
+    -------
+    dict
+        {package_name: {status, issues, local, git, remote}}
+        Only packages with mismatches are included.
+    """
+    return get_mismatches(packages)
+
+
+def fix_local(
+    packages: list[str] | None = None,
+    confirm: bool = False,
+    config: DevConfig | None = None,
+) -> dict[str, Any]:
+    """Fix local mismatches only: pip install -e for each package.
+
+    Parameters
+    ----------
+    packages : list[str] | None
+        Package names to fix. None = auto-detect from mismatches.
+    confirm : bool
+        If False (default), preview only.
+    config : DevConfig | None
+        Configuration.
+
+    Returns
+    -------
+    dict
+        {package_name: {status, output|commands}}
+    """
+    if config is None:
+        config = load_config()
+
+    if packages is None:
+        mismatches = get_mismatches()
+        packages = _find_local_mismatches(mismatches)
+
+    if not packages:
+        return {}
+
+    return sync_local(packages=packages, confirm=confirm, config=config)
+
+
+def fix_remote(
+    hosts: list[str] | None = None,
+    packages: list[str] | None = None,
+    install: bool = True,
+    confirm: bool = False,
+    config: DevConfig | None = None,
+) -> dict[str, Any]:
+    """Fix remote mismatches only: git pull + pip install on hosts.
+
+    Parameters
+    ----------
+    hosts : list[str] | None
+        Host names. None = all enabled hosts.
+    packages : list[str] | None
+        Package names. None = auto-detect from mismatches.
+    install : bool
+        Run pip install after git pull (default True).
+    confirm : bool
+        If False (default), preview only.
+    config : DevConfig | None
+        Configuration.
+
+    Returns
+    -------
+    dict
+        {host: {package: {status, output|commands}}}
+    """
+    if config is None:
+        config = load_config()
+
+    if packages is None:
+        mismatches = get_mismatches()
+        packages = list(mismatches.keys())
+
+    if not packages:
+        return {}
+
+    return sync_all(
+        hosts=hosts,
+        packages=packages,
+        stash=True,
+        install=install,
+        confirm=confirm,
+        config=config,
+    )
+
+
+def fix_init_version(
+    package_path: str | Path,
+    confirm: bool = False,
+) -> dict[str, Any]:
+    """Sync __init__.py __version__ with pyproject.toml version.
+
+    Parameters
+    ----------
+    package_path : str | Path
+        Path to package root (containing pyproject.toml).
+    confirm : bool
+        If False, preview only.
+
+    Returns
+    -------
+    dict
+        {toml_version, init_version, init_path, action, status}
+    """
+    import re
+
+    path = Path(package_path)
+    toml_path = path / "pyproject.toml"
+
+    if not toml_path.exists():
+        return {"status": "error", "error": "pyproject.toml not found"}
+
+    # Read toml version
+    toml_text = toml_path.read_text()
+    m = re.search(r'^version\s*=\s*"([^"]+)"', toml_text, re.MULTILINE)
+    if not m:
+        return {"status": "error", "error": "version not found in pyproject.toml"}
+    toml_version = m.group(1)
+
+    # Find __init__.py with __version__
+    init_files = list(path.glob("src/**/__init__.py"))
+    for init_path in init_files:
+        init_text = init_path.read_text()
+        vm = re.search(r'^__version__\s*=\s*"([^"]+)"', init_text, re.MULTILINE)
+        if vm:
+            init_version = vm.group(1)
+            if init_version == toml_version:
+                return {
+                    "toml_version": toml_version,
+                    "init_version": init_version,
+                    "init_path": str(init_path),
+                    "action": "skip",
+                    "status": "ok",
+                }
+
+            if confirm:
+                new_text = re.sub(
+                    r'^__version__\s*=\s*"[^"]+"',
+                    f'__version__ = "{toml_version}"',
+                    init_text,
+                    count=1,
+                    flags=re.MULTILINE,
+                )
+                init_path.write_text(new_text)
+
+            return {
+                "toml_version": toml_version,
+                "init_version": init_version,
+                "init_path": str(init_path),
+                "action": "updated" if confirm else "would_update",
+                "status": "ok",
+            }
+
+    return {"toml_version": toml_version, "action": "no_init_version", "status": "ok"}
+
+
+def verify_versions(
+    packages: list[str] | None = None,
+) -> dict[str, str]:
+    """Verify all versions are aligned after fixes.
+
+    Parameters
+    ----------
+    packages : list[str] | None
+        Package names. None = all.
+
+    Returns
+    -------
+    dict
+        {package_name: "ok" | "mismatch: <details>"}
+    """
+    mismatches = get_mismatches(packages)
+    from .versions import list_versions
+
+    all_versions = list_versions(packages)
+    result: dict[str, str] = {}
+    for pkg, info in all_versions.items():
+        if pkg in mismatches:
+            issues = mismatches[pkg].get("issues", [])
+            result[pkg] = f"mismatch: {'; '.join(issues)}"
+        else:
+            result[pkg] = "ok"
+    return result
+
+
+def bump_version(
+    package_path: str | Path,
+    new_version: str,
+    confirm: bool = False,
+) -> dict[str, Any]:
+    """Bump version in pyproject.toml and __init__.py.
+
+    Parameters
+    ----------
+    package_path : str | Path
+        Path to package root (containing pyproject.toml).
+    new_version : str
+        New version string (e.g. "0.5.0").
+    confirm : bool
+        If False, preview only.
+
+    Returns
+    -------
+    dict
+        {old_version, new_version, toml_updated, init_updated, status}
+    """
+    import re
+
+    path = Path(package_path)
+    toml_path = path / "pyproject.toml"
+
+    if not toml_path.exists():
+        return {"status": "error", "error": "pyproject.toml not found"}
+
+    toml_text = toml_path.read_text()
+    m = re.search(r'^version\s*=\s*"([^"]+)"', toml_text, re.MULTILINE)
+    if not m:
+        return {"status": "error", "error": "version not found in pyproject.toml"}
+
+    old_version = m.group(1)
+    if old_version == new_version:
+        return {
+            "old_version": old_version,
+            "new_version": new_version,
+            "action": "skip",
+            "status": "ok",
+        }
+
+    result: dict[str, Any] = {
+        "old_version": old_version,
+        "new_version": new_version,
+        "toml_updated": False,
+        "init_updated": False,
+        "status": "ok",
+    }
+
+    if confirm:
+        new_toml = re.sub(
+            r'^version\s*=\s*"[^"]+"',
+            f'version = "{new_version}"',
+            toml_text,
+            count=1,
+            flags=re.MULTILINE,
+        )
+        toml_path.write_text(new_toml)
+        result["toml_updated"] = True
+
+        # Also update __init__.py
+        init_result = fix_init_version(package_path, confirm=True)
+        result["init_updated"] = init_result.get("action") == "updated"
+    else:
+        result["action"] = "would_bump"
+
+    return result
+
+
+def determine_bump_type(
+    package_path: str | Path,
+) -> dict[str, Any]:
+    """Check diff since last tag and classify as minor/patch/skip.
+
+    Parameters
+    ----------
+    package_path : str | Path
+        Path to package root (git repo).
+
+    Returns
+    -------
+    dict
+        {has_diff, commits_since_tag, has_feat, bump_type, current_version, suggested_version}
+    """
+    import re
+    import subprocess
+
+    path = Path(package_path)
+
+    # Get current version
+    toml_path = path / "pyproject.toml"
+    current_version = None
+    if toml_path.exists():
+        m = re.search(
+            r'^version\s*=\s*"([^"]+)"',
+            toml_path.read_text(),
+            re.MULTILINE,
+        )
+        if m:
+            current_version = m.group(1)
+
+    # Get last tag
+    try:
+        tag_result = subprocess.run(
+            ["git", "describe", "--tags", "--abbrev=0", "--match", "v*"],
+            cwd=path,
+            capture_output=True,
+            text=True,
+            timeout=5,
+        )
+        if tag_result.returncode != 0:
+            return {
+                "has_diff": True,
+                "commits_since_tag": None,
+                "bump_type": "patch",
+                "current_version": current_version,
+                "error": "no tags found",
+            }
+        last_tag = tag_result.stdout.strip()
+    except Exception as e:
+        return {"status": "error", "error": str(e)}
+
+    # Check diff
+    diff_result = subprocess.run(
+        ["git", "diff", f"{last_tag}..HEAD", "--stat"],
+        cwd=path,
+        capture_output=True,
+        text=True,
+        timeout=10,
+    )
+    has_diff = bool(diff_result.stdout.strip())
+
+    if not has_diff:
+        return {
+            "has_diff": False,
+            "commits_since_tag": 0,
+            "bump_type": "skip",
+            "current_version": current_version,
+            "last_tag": last_tag,
+        }
+
+    # Count commits and check for feat:
+    log_result = subprocess.run(
+        ["git", "log", f"{last_tag}..HEAD", "--oneline"],
+        cwd=path,
+        capture_output=True,
+        text=True,
+        timeout=10,
+    )
+    commits = log_result.stdout.strip().splitlines()
+    has_feat = any("feat:" in line or "feat(" in line for line in commits)
+    bump_type = "minor" if has_feat else "patch"
+
+    # Suggest version
+    suggested = None
+    if current_version:
+        parts = current_version.split(".")
+        if len(parts) == 3:
+            major, minor, patch = (
+                int(parts[0]),
+                int(parts[1]),
+                int(parts[2].split("-")[0]),
+            )
+            if bump_type == "minor":
+                suggested = f"{major}.{minor + 1}.0"
+            else:
+                suggested = f"{major}.{minor}.{patch + 1}"
+
+    return {
+        "has_diff": True,
+        "commits_since_tag": len(commits),
+        "has_feat": has_feat,
+        "bump_type": bump_type,
+        "current_version": current_version,
+        "suggested_version": suggested,
+        "last_tag": last_tag,
+    }
+
+
+# --- Combined (backward compat) ---
+
+
+
+[docs] +def fix_mismatches( + hosts: list[str] | None = None, + packages: list[str] | None = None, + local: bool = True, + remote: bool = True, + confirm: bool = False, + config: DevConfig | None = None, +) -> dict[str, Any]: + """Detect version mismatches and fix them. + + Combines detect + fix_local + fix_remote. Kept for backward compatibility. + Prefer using the individual functions for clarity. + + Safety: defaults to preview only. Pass confirm=True to execute. + """ + if config is None: + config = load_config() + + mismatches = get_mismatches(packages) + mismatch_names = list(mismatches.keys()) if not packages else packages + + result: dict[str, Any] = { + "detected": { + pkg: {"status": info.get("status"), "issues": info.get("issues", [])} + for pkg, info in mismatches.items() + }, + "local_fixes": {}, + "remote_fixes": {}, + "summary": {"detected": len(mismatches), "local_fixed": 0, "remote_fixed": 0}, + } + + if not mismatch_names: + return result + + if local: + result["local_fixes"] = fix_local( + packages=_find_local_mismatches(mismatches), + confirm=confirm, + config=config, + ) + if confirm: + result["summary"]["local_fixed"] = sum( + 1 for r in result["local_fixes"].values() if r.get("status") == "ok" + ) + + if remote: + result["remote_fixes"] = fix_remote( + hosts=hosts, + packages=mismatch_names, + confirm=confirm, + config=config, + ) + if confirm: + for host_results in result["remote_fixes"].values(): + if isinstance(host_results, dict): + result["summary"]["remote_fixed"] += sum( + 1 + for r in host_results.values() + if isinstance(r, dict) and r.get("status") == "ok" + ) + + return result
+ + + +def _find_local_mismatches(mismatches: dict[str, Any]) -> list[str]: + """Extract package names where local installed != toml version.""" + to_fix = [] + for pkg, info in mismatches.items(): + lv = info.get("local", {}) + toml = lv.get("pyproject_toml") + installed = lv.get("installed") + if toml and installed and toml != installed: + to_fix.append(pkg) + elif toml and not installed: + to_fix.append(pkg) + return to_fix + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_dev/_release/versions.html b/src/scitex/_sphinx_html/_modules/scitex_dev/_release/versions.html new file mode 100644 index 000000000..77883284c --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_dev/_release/versions.html @@ -0,0 +1,1157 @@ + + + + + + + + scitex_dev._release.versions — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_dev._release.versions

+#!/usr/bin/env python3
+# Timestamp: 2026-02-02
+# File: scitex_dev/versions.py
+
+"""Core version checking logic for the scitex ecosystem."""
+
+from __future__ import annotations
+
+import re
+import subprocess
+from pathlib import Path
+from typing import Any
+
+from .._ecosystem import ECOSYSTEM, get_all_packages, get_local_path
+
+
+def get_version_from_toml(path: Path) -> str | None:
+    """Read version from pyproject.toml."""
+    toml_path = path / "pyproject.toml"
+    if not toml_path.exists():
+        return None
+
+    try:
+        # Python 3.11+
+        import tomllib
+
+        with open(toml_path, "rb") as f:
+            data = tomllib.load(f)
+    except ImportError:
+        try:
+            import tomli
+
+            with open(toml_path, "rb") as f:
+                data = tomli.load(f)
+        except ImportError:
+            # Fallback: regex parse
+            content = toml_path.read_text()
+            match = re.search(
+                r'^version\s*=\s*["\']([^"\']+)["\']', content, re.MULTILINE
+            )
+            return match.group(1) if match else None
+
+    return data.get("project", {}).get("version")
+
+
+def get_version_installed(package: str) -> str | None:
+    """Get version from importlib.metadata."""
+    try:
+        from importlib.metadata import version
+
+        return version(package)
+    except Exception:
+        return None
+
+
+def get_commits_since_tag(path: Path) -> int | None:
+    """Count commits since latest version tag. Returns None if no tags."""
+    if not path.exists():
+        return None
+    try:
+        result = subprocess.run(
+            ["git", "rev-list", "--count", "HEAD", "--not", "--tags=v*"],
+            cwd=path,
+            capture_output=True,
+            text=True,
+            timeout=5,
+        )
+        if result.returncode == 0:
+            return int(result.stdout.strip())
+    except Exception:
+        pass
+
+    # Fallback: count via log
+    try:
+        tag_result = subprocess.run(
+            ["git", "describe", "--tags", "--abbrev=0", "--match", "v*"],
+            cwd=path,
+            capture_output=True,
+            text=True,
+            timeout=5,
+        )
+        if tag_result.returncode != 0:
+            return None
+        tag = tag_result.stdout.strip()
+        log_result = subprocess.run(
+            ["git", "rev-list", "--count", f"{tag}..HEAD"],
+            cwd=path,
+            capture_output=True,
+            text=True,
+            timeout=5,
+        )
+        if log_result.returncode == 0:
+            return int(log_result.stdout.strip())
+    except Exception:
+        pass
+    return None
+
+
+def get_git_latest_tag(path: Path) -> str | None:
+    """Get latest git tag (version tags only)."""
+    if not path.exists():
+        return None
+
+    try:
+        result = subprocess.run(
+            ["git", "describe", "--tags", "--abbrev=0", "--match", "v*"],
+            cwd=path,
+            capture_output=True,
+            text=True,
+            timeout=5,
+        )
+        if result.returncode == 0:
+            return result.stdout.strip()
+    except Exception:
+        pass
+
+    # Fallback: list all tags
+    try:
+        result = subprocess.run(
+            ["git", "tag", "-l", "v*", "--sort=-v:refname"],
+            cwd=path,
+            capture_output=True,
+            text=True,
+            timeout=5,
+        )
+        if result.returncode == 0 and result.stdout.strip():
+            return result.stdout.strip().split("\n")[0]
+    except Exception:
+        pass
+
+    return None
+
+
+def get_git_branch(path: Path) -> str | None:
+    """Get current git branch."""
+    if not path.exists():
+        return None
+
+    try:
+        result = subprocess.run(
+            ["git", "rev-parse", "--abbrev-ref", "HEAD"],
+            cwd=path,
+            capture_output=True,
+            text=True,
+            timeout=5,
+        )
+        if result.returncode == 0:
+            return result.stdout.strip()
+    except Exception:
+        pass
+
+    return None
+
+
+def get_git_status(path: Path) -> dict[str, Any] | None:
+    """Get git worktree status (dirty/clean, ahead/behind remote).
+
+    Returns
+    -------
+    dict or None
+        Keys: dirty (bool), ahead (int), behind (int), short_hash (str).
+        Returns None if path doesn't exist or is not a git repo.
+    """
+    if not path.exists():
+        return None
+
+    info: dict[str, Any] = {"dirty": False, "ahead": 0, "behind": 0, "short_hash": None}
+
+    # Check if dirty (uncommitted changes)
+    try:
+        result = subprocess.run(
+            ["git", "status", "--porcelain"],
+            cwd=path,
+            capture_output=True,
+            text=True,
+            timeout=5,
+        )
+        if result.returncode == 0:
+            info["dirty"] = bool(result.stdout.strip())
+    except Exception:
+        pass
+
+    # Get short commit hash
+    try:
+        result = subprocess.run(
+            ["git", "rev-parse", "--short", "HEAD"],
+            cwd=path,
+            capture_output=True,
+            text=True,
+            timeout=5,
+        )
+        if result.returncode == 0:
+            info["short_hash"] = result.stdout.strip()
+    except Exception:
+        pass
+
+    # Ahead/behind upstream
+    try:
+        result = subprocess.run(
+            ["git", "rev-list", "--left-right", "--count", "HEAD...@{upstream}"],
+            cwd=path,
+            capture_output=True,
+            text=True,
+            timeout=5,
+        )
+        if result.returncode == 0:
+            parts = result.stdout.strip().split()
+            if len(parts) == 2:
+                info["ahead"] = int(parts[0])
+                info["behind"] = int(parts[1])
+    except Exception:
+        pass
+
+    return info
+
+
+def get_pypi_version(package: str) -> str | None:
+    """Fetch latest version from PyPI API."""
+    try:
+        import urllib.request
+
+        url = f"https://pypi.org/pypi/{package}/json"
+        with urllib.request.urlopen(url, timeout=5) as response:
+            import json
+
+            data = json.loads(response.read().decode())
+            return data.get("info", {}).get("version")
+    except Exception:
+        return None
+
+
+def _normalize_version(v: str | None) -> str | None:
+    """Normalize version string (strip v prefix)."""
+    if v is None:
+        return None
+    return v.lstrip("v")
+
+
+def _pep440_equal(v1: str | None, v2: str | None) -> bool:
+    """Compare two version strings using PEP 440 normalization.
+
+    Treats e.g. '0.10.3-alpha' and '0.10.3a0' as equal.
+    """
+    if v1 is None or v2 is None:
+        return v1 == v2
+    from packaging.version import InvalidVersion, Version
+
+    try:
+        return Version(_normalize_version(v1)) == Version(_normalize_version(v2))
+    except InvalidVersion:
+        return _normalize_version(v1) == _normalize_version(v2)
+
+
+def _compare_versions(v1: str | None, v2: str | None) -> int:
+    """Compare two version strings. Returns -1, 0, or 1."""
+    if v1 is None or v2 is None:
+        return 0
+
+    from packaging.version import Version
+
+    try:
+        ver1 = Version(_normalize_version(v1))
+        ver2 = Version(_normalize_version(v2))
+        if ver1 < ver2:
+            return -1
+        if ver1 > ver2:
+            return 1
+        return 0
+    except Exception:
+        # Fallback: string comparison
+        return 0
+
+
+def _determine_status(info: dict[str, Any]) -> tuple[str, list[str]]:
+    """Determine version status and issues."""
+    issues = []
+
+    toml_ver = info.get("local", {}).get("pyproject_toml")
+    installed_ver = info.get("local", {}).get("installed")
+    tag_ver = _normalize_version(info.get("git", {}).get("latest_tag"))
+    pypi_ver = info.get("remote", {}).get("pypi")
+
+    # Check local consistency (PEP 440: '0.10.3-alpha' == '0.10.3a0')
+    if toml_ver and installed_ver and not _pep440_equal(toml_ver, installed_ver):
+        issues.append(f"pyproject.toml ({toml_ver}) != installed ({installed_ver})")
+
+    # Check if toml matches tag
+    if toml_ver and tag_ver and not _pep440_equal(toml_ver, tag_ver):
+        issues.append(f"pyproject.toml ({toml_ver}) != git tag ({tag_ver})")
+
+    # Check pypi status
+    if toml_ver and pypi_ver:
+        cmp = _compare_versions(toml_ver, pypi_ver)
+        if cmp > 0:
+            issues.append(f"local ({toml_ver}) > pypi ({pypi_ver}) - ready to release")
+            return "unreleased", issues
+        if cmp < 0:
+            issues.append(f"local ({toml_ver}) < pypi ({pypi_ver}) - outdated")
+            return "outdated", issues
+
+    # Check code-version mismatch (commits since last tag)
+    git_info = info.get("git", {})
+    commits_since = git_info.get("commits_since_tag")
+    if commits_since and commits_since > 0 and toml_ver and tag_ver:
+        if _pep440_equal(toml_ver, tag_ver):
+            issues.append(
+                f"{commits_since} commit(s) since {tag_ver} but version not bumped"
+            )
+
+    # Check git worktree status
+    if git_info.get("dirty"):
+        issues.append("uncommitted changes")
+    ahead = git_info.get("ahead", 0)
+    behind = git_info.get("behind", 0)
+    if ahead:
+        issues.append(f"{ahead} commit(s) ahead of remote")
+    if behind:
+        issues.append(f"{behind} commit(s) behind remote")
+
+    if issues:
+        return "mismatch", issues
+
+    if not toml_ver:
+        return "unavailable", ["package not found locally"]
+
+    return "ok", []
+
+
+
+[docs] +def list_versions(packages: list[str] | None = None) -> dict[str, Any]: + """List versions for all ecosystem packages. + + Parameters + ---------- + packages : list[str] | None + List of package names to check. If None, checks all ecosystem packages. + + Returns + ------- + dict + Version information for each package. + """ + if packages is None: + packages = get_all_packages() + + result = {} + for pkg in packages: + if pkg not in ECOSYSTEM: + result[pkg] = {"status": "unknown", "issues": [f"'{pkg}' not in ecosystem"]} + continue + + info: dict[str, Any] = {"local": {}, "git": {}, "remote": {}} + local_path = get_local_path(pkg) + pypi_name = ECOSYSTEM[pkg].get("pypi_name", pkg) + + # Local sources + if local_path and local_path.exists(): + info["local"]["pyproject_toml"] = get_version_from_toml(local_path) + info["local"]["installed"] = get_version_installed(pypi_name) + + # Git sources + if local_path and local_path.exists(): + info["git"]["latest_tag"] = get_git_latest_tag(local_path) + info["git"]["branch"] = get_git_branch(local_path) + info["git"]["commits_since_tag"] = get_commits_since_tag(local_path) + git_status = get_git_status(local_path) + if git_status: + info["git"]["dirty"] = git_status["dirty"] + info["git"]["ahead"] = git_status["ahead"] + info["git"]["behind"] = git_status["behind"] + info["git"]["short_hash"] = git_status["short_hash"] + + # Remote sources + info["remote"]["pypi"] = get_pypi_version(pypi_name) + + # Determine status + status, issues = _determine_status(info) + info["status"] = status + info["issues"] = issues + + result[pkg] = info + + return result
+ + + +
+[docs] +def check_versions(packages: list[str] | None = None) -> dict[str, Any]: + """Check version consistency and return detailed status. + + Parameters + ---------- + packages : list[str] | None + List of package names to check. If None, checks all ecosystem packages. + + Returns + ------- + dict + Detailed version check results with overall summary. + """ + versions = list_versions(packages) + + summary = { + "total": len(versions), + "ok": 0, + "mismatch": 0, + "unreleased": 0, + "outdated": 0, + "unavailable": 0, + "unknown": 0, + } + + for _pkg, info in versions.items(): + status = info.get("status", "unknown") + if status in summary: + summary[status] += 1 + + return {"packages": versions, "summary": summary}
+ + + +
+[docs] +def get_mismatches(packages: list[str] | None = None) -> dict[str, Any]: + """Return packages with non-ok status and their issues. + + Parameters + ---------- + packages : list[str] | None + Package names to check. None = all ecosystem packages. + + Returns + ------- + dict + {package_name: {status, issues, local, git, remote}} for non-ok packages. + """ + versions = list_versions(packages) + return { + pkg: info + for pkg, info in versions.items() + if info.get("status") not in ("ok", "unavailable") + }
+ + + +
+[docs] +def get_ecosystem_versions( + packages: list[str] | None = None, +) -> dict[str, str | None]: + """Return a flat `{pkg_name: installed_version}` dict for the ecosystem. + + Thin wrapper over `list_versions` for consumers that just need the + installed-version string (Django health endpoints, Docker HEALTHCHECK + scripts, dashboards). Skips all the git / PyPI / pyproject cross- + check detail. + + Parameters + ---------- + packages : list[str] | None + Package names to check. None = all ecosystem packages. + + Returns + ------- + dict[str, str | None] + ``{"scitex": "2.27.3", "figrecipe": "0.28.1", "scitex-io": None}``. + None means the package is not installed. + + Examples + -------- + >>> from scitex_dev import get_ecosystem_versions + >>> vers = get_ecosystem_versions() + >>> vers["scitex"] + '2.27.3' + """ + details = list_versions(packages) + return { + pkg: (info.get("local", {}) or {}).get("installed") + for pkg, info in details.items() + }
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_dev/_sync/_local.html b/src/scitex/_sphinx_html/_modules/scitex_dev/_sync/_local.html new file mode 100644 index 000000000..5967469d9 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_dev/_sync/_local.html @@ -0,0 +1,1206 @@ + + + + + + + + scitex_dev._sync._local — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_dev._sync._local

+#!/usr/bin/env python3
+# Timestamp: 2026-02-24
+# File: scitex_dev/sync.py
+
+"""Ecosystem package sync across local and remote hosts.
+
+Safety model (like bulk_rename):
+  - All operations default to dry_run=True (preview only).
+  - Pass confirm=True to actually execute.
+  - CLI requires --confirm flag.
+  - MCP tool requires confirm=True parameter.
+"""
+
+from __future__ import annotations
+
+import subprocess
+import sys
+from concurrent.futures import ThreadPoolExecutor, as_completed
+from pathlib import Path
+from typing import Any
+
+from .._core.config import DevConfig, HostConfig, get_enabled_hosts, load_config
+
+# ---------------------------------------------------------------------------
+# SSH helpers
+# ---------------------------------------------------------------------------
+
+
+def _build_ssh_args(host: HostConfig) -> list[str]:
+    """Build SSH command prefix for a host."""
+    args = ["ssh"]
+    if host.ssh_key:
+        args.extend(["-i", host.ssh_key])
+    if host.port != 22:
+        args.extend(["-p", str(host.port)])
+    args.extend(
+        [
+            "-o",
+            "BatchMode=yes",
+            "-o",
+            "StrictHostKeyChecking=accept-new",
+            "-o",
+            "ConnectTimeout=10",
+        ]
+    )
+    args.append(f"{host.user}@{host.hostname}")
+    return args
+
+
+def _get_host_packages(host: HostConfig, config: DevConfig) -> list[tuple[str, str]]:
+    """Get (package_name, remote_dir_name) pairs for a host.
+
+    Resolution: legacy ``host.packages`` allow-list wins; otherwise
+    default to all ecosystem packages minus ``host.exclude``.
+    """
+    pkg_map = {p.name: p for p in config.packages}
+    if host.packages:
+        names = list(host.packages)
+    else:
+        excluded = set(host.exclude or [])
+        names = [p.name for p in config.packages if p.name not in excluded]
+    out = []
+    for name in names:
+        pkg = pkg_map.get(name)
+        if pkg and pkg.local_path:
+            out.append((name, Path(pkg.local_path).expanduser().name))
+    return out
+
+
+def _build_sync_commands(
+    host: HostConfig, dir_name: str, stash: bool, install: bool
+) -> list[str]:
+    """Build the shell commands that would be run for a package."""
+    base = f"{host.remote_base}/{dir_name}"
+    cmds = [f"cd {base}"]
+    if install:
+        # Ensure .venv symlink exists so pip resolves to the right env
+        cmds.append("test -e .venv || ln -s ~/.venv .venv 2>/dev/null || true")
+    if stash:
+        cmds.append("git stash")
+    cmds.append("git pull")
+    if install:
+        cmds.append(f"{host.pip_bin} install -e . -q")
+    if stash:
+        cmds.append("git stash pop 2>/dev/null || true")
+    return cmds
+
+
+def _check_ahead_state(
+    host: HostConfig,
+    dir_name: str,
+    *,
+    subprocess_run=None,
+) -> dict[str, Any]:
+    """Inspect the remote working copy's position vs. its upstream.
+
+    Returns a dict with ``status`` in:
+      - ``clean``: up-to-date or only behind upstream (safe to pull).
+      - ``ahead``: remote-side has unpushed commits — fast-forward pull
+        would be blocked; skip to avoid clobbering local work.
+      - ``diverged``: both ahead and behind — needs human decision.
+      - ``missing``: repo dir or git metadata absent.
+      - ``error``: SSH/git failure; see ``error`` field.
+
+    Also returns ``local_ahead`` (remote-side commits ahead of
+    ``@{u}``) and ``remote_ahead`` (``@{u}`` commits ahead of HEAD).
+    "Local" here is confusingly the *remote host's* working copy —
+    the caller is on some other machine.
+    """
+    base = f"{host.remote_base}/{dir_name}"
+    # Single-line remote script — uses unique sentinels so output parsing
+    # survives line-wrap/colorization from git or shell integrations.
+    remote_cmd = (
+        f"cd {base} 2>/dev/null || {{ echo SACDEV_MISSING; exit 0; }}; "
+        "if [ ! -d .git ]; then echo SACDEV_MISSING; exit 0; fi; "
+        "git fetch -q 2>/dev/null || true; "
+        'la=$(git rev-list --count "@{u}..HEAD" 2>/dev/null || echo 0); '
+        'ra=$(git rev-list --count "HEAD..@{u}" 2>/dev/null || echo 0); '
+        "echo SACDEV_STATE la=$la ra=$ra"
+    )
+    ssh_args = _build_ssh_args(host)
+    ssh_args.append(remote_cmd)
+    runner = subprocess_run if subprocess_run is not None else subprocess.run
+    try:
+        result = runner(ssh_args, capture_output=True, text=True, timeout=60)
+        stdout = result.stdout.strip()
+        if result.returncode != 0:
+            return {
+                "status": "error",
+                "error": (result.stderr.strip() or f"exit {result.returncode}"),
+            }
+        if "SACDEV_MISSING" in stdout:
+            return {"status": "missing"}
+        # Parse: "SACDEV_STATE la=X ra=Y"
+        la, ra = 0, 0
+        for tok in stdout.split():
+            if tok.startswith("la="):
+                la = int(tok[3:] or 0)
+            elif tok.startswith("ra="):
+                ra = int(tok[3:] or 0)
+        if la > 0 and ra > 0:
+            status = "diverged"
+        elif la > 0:
+            status = "ahead"
+        else:
+            status = "clean"
+        return {"status": status, "local_ahead": la, "remote_ahead": ra}
+    except subprocess.TimeoutExpired:
+        return {"status": "error", "error": "ahead-check SSH timed out (60s)"}
+    except Exception as e:
+        return {"status": "error", "error": str(e)}
+
+
+def _sync_one_package(
+    host: HostConfig,
+    dir_name: str,
+    stash: bool,
+    install: bool,
+    safe: bool = True,
+    *,
+    subprocess_run=None,
+    check_ahead_state_fn=None,
+) -> dict[str, Any]:
+    """Sync a single package on a remote host.
+
+    When ``safe`` is True (default), first check whether the remote
+    working copy has unpushed commits; if so, skip to avoid clobbering.
+    This matches the user instruction: if remote is ahead, compare and
+    skip when a decision is unclear.
+    """
+    check_ahead = (
+        check_ahead_state_fn if check_ahead_state_fn is not None else _check_ahead_state
+    )
+    runner = subprocess_run if subprocess_run is not None else subprocess.run
+    if safe:
+        ahead = check_ahead(host, dir_name)
+        if ahead["status"] in {"ahead", "diverged"}:
+            return {
+                "status": f"skipped_{ahead['status']}",
+                "local_ahead": ahead.get("local_ahead", 0),
+                "remote_ahead": ahead.get("remote_ahead", 0),
+                "reason": (
+                    "remote working copy has unpushed commits; resolve manually "
+                    "(push / rebase / reset) then re-run sync"
+                ),
+            }
+        if ahead["status"] == "missing":
+            return {"status": "missing", "reason": "package dir not on remote"}
+        if ahead["status"] == "error":
+            return {
+                "status": "error",
+                "error": ahead.get("error", "ahead-check failed"),
+            }
+
+    cmds = _build_sync_commands(host, dir_name, stash, install)
+    remote_cmd = " && ".join(cmds)
+
+    ssh_args = _build_ssh_args(host)
+    ssh_args.append(remote_cmd)
+
+    try:
+        result = runner(ssh_args, capture_output=True, text=True, timeout=120)
+        stdout = result.stdout.strip()
+        stderr = result.stderr.strip()
+
+        if result.returncode == 0:
+            return {"status": "ok", "output": stdout}
+        return {
+            "status": "error",
+            "output": stdout,
+            "error": stderr or f"exit code {result.returncode}",
+        }
+    except subprocess.TimeoutExpired:
+        return {"status": "timeout", "error": "SSH command timed out (120s)"}
+    except Exception as e:
+        return {"status": "error", "error": str(e)}
+
+
+# ---------------------------------------------------------------------------
+# Public API — all default to dry_run=True (safe preview)
+# ---------------------------------------------------------------------------
+
+
+
+[docs] +def sync_host( + host: HostConfig, + packages: list[str] | None = None, + stash: bool = True, + install: bool = True, + safe: bool = True, + confirm: bool = False, + config: DevConfig | None = None, + *, + host_packages_fn=None, +) -> dict[str, Any]: + """Sync packages to a remote host via SSH. + + Safety: defaults to preview only. Pass confirm=True to execute. + + Steps per package: ahead-check (if safe), git stash, git pull, + pip install -e ., git stash pop. + + Parameters + ---------- + host : HostConfig + Target host configuration. + packages : list[str] | None + Package names to sync. None = use host's configured packages. + stash : bool + Git stash before pull (default True). + install : bool + Pip install after pull (default True). + safe : bool + If True (default), pre-check each remote working copy and skip + packages whose HEAD is ahead of / diverged from upstream so we + never clobber unpushed commits. + confirm : bool + If False (default), preview only (dry run). + If True, execute the sync operation. + config : DevConfig | None + Configuration. Loaded from default if None. + + Returns + ------- + dict + Per-package results: {package: {status, commands|output, error}}. + ``status`` includes ``ok``, ``skipped_ahead``, ``skipped_diverged``, + ``missing``, ``error``, ``timeout``, or ``dry_run``. + """ + if config is None: + config = load_config() + + get_host_pkgs = ( + host_packages_fn if host_packages_fn is not None else _get_host_packages + ) + host_pkgs = get_host_pkgs(host, config) + if packages: + host_pkgs = [(n, d) for n, d in host_pkgs if n in packages] + + if not confirm: + return { + name: { + "status": "dry_run", + "commands": _build_sync_commands(host, dir_name, stash, install), + "safe_check": safe, + } + for name, dir_name in host_pkgs + } + + # Parallel package sync within a single host + results: dict[str, Any] = {} + with ThreadPoolExecutor(max_workers=4) as executor: + futures = { + executor.submit( + _sync_one_package, host, dir_name, stash, install, safe + ): name + for name, dir_name in host_pkgs + } + for future in as_completed(futures): + name = futures[future] + try: + results[name] = future.result() + except Exception as e: + results[name] = {"status": "error", "error": str(e)} + return results
+ + + +
+[docs] +def sync_all( + hosts: list[str] | None = None, + packages: list[str] | None = None, + stash: bool = True, + install: bool = True, + safe: bool = True, + confirm: bool = False, + config: DevConfig | None = None, + *, + sync_host_fn=None, + enabled_hosts_fn=None, +) -> dict[str, Any]: + """Sync packages across all enabled hosts. + + Safety: defaults to preview only. Pass confirm=True to execute. + Parallel: hosts are synced concurrently by default. + + Parameters + ---------- + hosts : list[str] | None + Host names to sync. None = all enabled hosts. + packages : list[str] | None + Package names. None = host-specific defaults. + stash : bool + Git stash before pull. + install : bool + Pip install after pull. + safe : bool + If True (default), skip packages whose remote working copy is + ahead of / diverged from upstream (never clobber unpushed work). + confirm : bool + If False (default), preview only (dry run). + If True, execute the sync operation. + config : DevConfig | None + Configuration. + + Returns + ------- + dict + {host_name: {package: result}}. + """ + if config is None: + config = load_config() + + enabled_fn = enabled_hosts_fn if enabled_hosts_fn is not None else get_enabled_hosts + do_sync_host = sync_host_fn if sync_host_fn is not None else sync_host + + enabled = enabled_fn(config) + if hosts: + enabled = [h for h in enabled if h.name in hosts] + + if not confirm: + # Dry-run: no SSH needed, compute locally + return { + host.name: do_sync_host( + host, + packages=packages, + stash=stash, + install=install, + safe=safe, + confirm=False, + config=config, + ) + for host in enabled + } + + # Execute: parallel across hosts + results: dict[str, Any] = {} + with ThreadPoolExecutor(max_workers=len(enabled) or 1) as executor: + futures = { + executor.submit( + do_sync_host, + host, + packages=packages, + stash=stash, + install=install, + safe=safe, + confirm=True, + config=config, + ): host.name + for host in enabled + } + for future in as_completed(futures): + host_name = futures[future] + try: + results[host_name] = future.result() + except Exception as e: + results[host_name] = {"error": str(e)} + return results
+ + + +def _install_one(pkg, path: Path) -> tuple[str, dict[str, Any]]: + """Run `pip install -e <path>` for one package; return (name, result).""" + try: + # sys.executable -m pip — bare `pip` finds the first one on PATH, + # which can be a system Python with stale metadata (spartan's + # /usr/bin/pip is Python 3.9 and fails on >=3.10 wheels). + result = subprocess.run( + [sys.executable, "-m", "pip", "install", "-e", str(path), "-q"], + capture_output=True, + text=True, + timeout=300, + ) + if result.returncode == 0: + return pkg.name, {"status": "ok", "output": result.stdout.strip()} + return pkg.name, {"status": "error", "error": result.stderr.strip()} + except Exception as e: + return pkg.name, {"status": "error", "error": str(e)} + + +
+[docs] +def sync_local( + packages: list[str] | None = None, + confirm: bool = False, + config: DevConfig | None = None, + jobs: int = 1, + on_progress=None, +) -> dict[str, Any]: + """Install all local editable packages. + + Safety: defaults to preview only. Pass confirm=True to execute. + + Parameters + ---------- + packages : list[str] | None + Package names. None = all configured packages. + confirm : bool + If False (default), preview only. + If True, execute pip install -e. + config : DevConfig | None + Configuration. + jobs : int + Parallel installs. 1 = serial (default). 0 or negative = all CPUs. + on_progress : callable | None + Optional callback ``f(idx, total, name, status, elapsed)`` invoked + as each package finishes. + + Returns + ------- + dict + {package: {status, output|commands}}. + """ + import os + import time + from concurrent.futures import ThreadPoolExecutor, as_completed + + if config is None: + config = load_config() + + targets = config.packages + if packages: + targets = [p for p in targets if p.name in packages] + + # Resolve installable targets (skip missing-path early) + work: list[tuple[Any, Path]] = [] + results: dict[str, Any] = {} + for pkg in targets: + if not pkg.local_path: + continue + path = Path(pkg.local_path).expanduser() + if not path.exists(): + results[pkg.name] = {"status": "skipped", "error": f"{path} not found"} + continue + if not confirm: + results[pkg.name] = { + "status": "dry_run", + "commands": [ + sys.executable, + "-m", + "pip", + "install", + "-e", + str(path), + "-q", + ], + } + continue + work.append((pkg, path)) + + if not work: + return results + + # Resolve --jobs + if jobs <= 0: + jobs = os.cpu_count() or 1 + jobs = max(1, min(jobs, len(work))) + + total = len(work) + started = {pkg.name: time.monotonic() for pkg, _ in work} + + if jobs == 1: + # Serial path — preserve deterministic ordering + for idx, (pkg, path) in enumerate(work, 1): + name, res = _install_one(pkg, path) + results[name] = res + if on_progress is not None: + on_progress( + idx, total, name, res["status"], time.monotonic() - started[name] + ) + else: + # Parallel path — pip install -e is mostly I/O (git, PyPI metadata) + with ThreadPoolExecutor(max_workers=jobs) as ex: + futures = { + ex.submit(_install_one, pkg, path): pkg.name for pkg, path in work + } + for idx, fut in enumerate(as_completed(futures), 1): + name, res = fut.result() + results[name] = res + if on_progress is not None: + on_progress( + idx, + total, + name, + res["status"], + time.monotonic() - started[name], + ) + + return results
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_dev/_sync/_remote.html b/src/scitex/_sphinx_html/_modules/scitex_dev/_sync/_remote.html new file mode 100644 index 000000000..0ee0d6d62 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_dev/_sync/_remote.html @@ -0,0 +1,1028 @@ + + + + + + + + scitex_dev._sync._remote — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_dev._sync._remote

+#!/usr/bin/env python3
+# Timestamp: 2026-02-26
+# File: scitex_dev/sync_remote.py
+
+"""Reverse sync operations: remote -> local.
+
+Complements sync.py (local -> remote) with:
+  - remote_diff(): Show uncommitted changes on remote hosts
+  - remote_commit(): Commit + push dirty changes on remote hosts
+  - pull_local(): Pull origin -> local repos
+
+Safety model (same as sync.py):
+  - All mutating operations default to confirm=False (preview only).
+  - Pass confirm=True to actually execute.
+"""
+
+from __future__ import annotations
+
+import subprocess
+from pathlib import Path
+from typing import Any
+
+from .._core.config import DevConfig, HostConfig, get_enabled_hosts, load_config
+from ._local import _build_ssh_args, _get_host_packages
+
+# ---------------------------------------------------------------------------
+# SSH helper (filters X11 noise)
+# ---------------------------------------------------------------------------
+
+
+def _run_ssh(host: HostConfig, remote_cmd: str, timeout: int = 120) -> dict[str, Any]:
+    """Run a single SSH command and return structured result."""
+    ssh_args = _build_ssh_args(host)
+    ssh_args.append(remote_cmd)
+    try:
+        result = subprocess.run(
+            ssh_args, capture_output=True, text=True, timeout=timeout
+        )
+        stdout = result.stdout.strip()
+        stderr = result.stderr.strip()
+        stderr_lines = [
+            line for line in stderr.splitlines() if "X11 forwarding" not in line
+        ]
+        stderr = "\n".join(stderr_lines).strip()
+        if result.returncode == 0:
+            return {"status": "ok", "output": stdout, "stderr": stderr}
+        return {
+            "status": "error",
+            "output": stdout,
+            "error": stderr or f"exit code {result.returncode}",
+        }
+    except subprocess.TimeoutExpired:
+        return {"status": "timeout", "error": f"SSH command timed out ({timeout}s)"}
+    except Exception as e:
+        return {"status": "error", "error": str(e)}
+
+
+# ---------------------------------------------------------------------------
+# Public API
+# ---------------------------------------------------------------------------
+
+
+
+[docs] +def remote_diff( + host: str | None = None, + packages: list[str] | None = None, + config: DevConfig | None = None, +) -> dict[str, Any]: + """Show git diff on remote host(s). Read-only operation. + + Parameters + ---------- + host : str | None + Host name. None = first enabled host. + packages : list[str] | None + Package names. None = host-configured defaults. + config : DevConfig | None + Configuration. + + Returns + ------- + dict + {host_name: {package: {status, files, diff_stat, diff}}}. + """ + if config is None: + config = load_config() + + enabled = get_enabled_hosts(config) + if host: + enabled = [h for h in enabled if h.name == host] + if not enabled: + return {"error": f"Host '{host}' not found or not enabled"} + + results: dict[str, Any] = {} + for h in enabled: + host_pkgs = _get_host_packages(h, config) + if packages: + host_pkgs = [(n, d) for n, d in host_pkgs if n in packages] + + pkg_results: dict[str, Any] = {} + for name, dir_name in host_pkgs: + base = f"{h.remote_base}/{dir_name}" + cmd = ( + f"cd {base} && " + f"echo '---STATUS---' && git status --short && " + f"echo '---STAT---' && git diff --stat && " + f"echo '---DIFF---' && git diff" + ) + r = _run_ssh(h, cmd) + if r["status"] == "ok": + output = r["output"] + parts = output.split("---DIFF---") + header = parts[0] if parts else "" + diff = parts[1].strip() if len(parts) > 1 else "" + + stat_parts = header.split("---STAT---") + status_text = ( + stat_parts[0].replace("---STATUS---", "").strip() + if stat_parts + else "" + ) + stat_text = stat_parts[1].strip() if len(stat_parts) > 1 else "" + + pkg_results[name] = { + "status": "dirty" if status_text else "clean", + "files": status_text, + "diff_stat": stat_text, + "diff": diff, + } + else: + pkg_results[name] = r + results[h.name] = pkg_results + return results
+ + + +
+[docs] +def remote_commit( + host: str, + packages: list[str] | None = None, + message: str | None = None, + push: bool = True, + confirm: bool = False, + config: DevConfig | None = None, +) -> dict[str, Any]: + """Commit dirty changes on a remote host and optionally push to origin. + + Safety: defaults to preview only. Pass confirm=True to execute. + + Parameters + ---------- + host : str + Host name (required). + packages : list[str] | None + Package names. None = host-configured defaults. + message : str | None + Commit message. Auto-generated if not provided. + push : bool + Push to origin after commit (default True). + confirm : bool + If False (default), preview only (dry run). + config : DevConfig | None + Configuration. + + Returns + ------- + dict + {package: {status, commands|output}}. + """ + if config is None: + config = load_config() + + enabled = get_enabled_hosts(config) + target = next((h for h in enabled if h.name == host), None) + if target is None: + return {"error": f"Host '{host}' not found or not enabled"} + + host_pkgs = _get_host_packages(target, config) + if packages: + host_pkgs = [(n, d) for n, d in host_pkgs if n in packages] + + results: dict[str, Any] = {} + for name, dir_name in host_pkgs: + base = f"{target.remote_base}/{dir_name}" + + if not confirm: + cmd = f"cd {base} && git status --short" + r = _run_ssh(target, cmd) + dirty_files = r.get("output", "").strip() if r["status"] == "ok" else "" + if not dirty_files: + results[name] = {"status": "clean", "message": "nothing to commit"} + continue + + msg = message or f"chore({name}): sync from {host}" + commit_cmds = [f"cd {base}", "git add -A", f'git commit -m "{msg}"'] + if push: + commit_cmds.append("git push origin $(git rev-parse --abbrev-ref HEAD)") + results[name] = { + "status": "dry_run", + "dirty_files": dirty_files, + "commands": commit_cmds, + } + continue + + # Execute: check if dirty first + status_r = _run_ssh(target, f"cd {base} && git status --short") + dirty = status_r.get("output", "").strip() if status_r["status"] == "ok" else "" + if not dirty: + results[name] = {"status": "clean", "message": "nothing to commit"} + continue + + msg = message or f"chore({name}): sync from {host}" + cmds = [f"cd {base}", "git add -A", f'git commit -m "{msg}"'] + if push: + cmds.append("git push origin $(git rev-parse --abbrev-ref HEAD)") + results[name] = _run_ssh(target, " && ".join(cmds)) + + return results
+ + + +
+[docs] +def pull_local( + packages: list[str] | None = None, + confirm: bool = False, + stash: bool = True, + config: DevConfig | None = None, +) -> dict[str, Any]: + """Pull latest from origin to local repos. + + Safety: defaults to preview only. Pass confirm=True to execute. + + Parameters + ---------- + packages : list[str] | None + Package names. None = all configured packages. + confirm : bool + If False (default), preview only. + stash : bool + If True (default), stash local changes before pull and pop after. + If False and repo is dirty, pull proceeds as-is (may fail). + config : DevConfig | None + Configuration. + + Returns + ------- + dict + {package: {status, output|commands, stashed}}. + """ + if config is None: + config = load_config() + + targets = config.packages + if packages: + targets = [p for p in targets if p.name in packages] + + results: dict[str, Any] = {} + for pkg in targets: + if not pkg.local_path: + continue + + path = Path(pkg.local_path).expanduser() + if not path.exists(): + results[pkg.name] = {"status": "skipped", "error": f"{path} not found"} + continue + + # Check if repo is dirty + status_result = subprocess.run( + ["git", "-C", str(path), "status", "--porcelain"], + capture_output=True, + text=True, + timeout=10, + ) + is_dirty = bool(status_result.stdout.strip()) + + if not confirm: + entry: dict[str, Any] = { + "status": "dry_run", + "commands": ["git", "-C", str(path), "pull", "origin"], + } + if is_dirty: + entry["dirty"] = True + if stash: + entry["note"] = "repo is dirty; would stash before pull" + results[pkg.name] = entry + continue + + try: + did_stash = False + if is_dirty and stash: + stash_result = subprocess.run( + ["git", "-C", str(path), "stash"], + capture_output=True, + text=True, + timeout=30, + ) + if stash_result.returncode != 0: + results[pkg.name] = { + "status": "error", + "error": stash_result.stderr.strip() or "git stash failed", + "stashed": False, + } + continue + did_stash = True + + result = subprocess.run( + ["git", "-C", str(path), "pull", "origin"], + capture_output=True, + text=True, + timeout=60, + ) + stdout = result.stdout.strip() + stderr = result.stderr.strip() + stderr_lines = [ + line for line in stderr.splitlines() if "X11 forwarding" not in line + ] + stderr = "\n".join(stderr_lines).strip() + + if did_stash: + pop_result = subprocess.run( + ["git", "-C", str(path), "stash", "pop"], + capture_output=True, + text=True, + timeout=30, + ) + if pop_result.returncode != 0: + results[pkg.name] = { + "status": "stash_conflict", + "output": stdout or stderr, + "stashed": True, + "error": pop_result.stderr.strip() + or "git stash pop failed; resolve conflicts manually", + } + continue + + if result.returncode == 0: + results[pkg.name] = { + "status": "ok", + "output": stdout or stderr, + "stashed": did_stash, + } + else: + results[pkg.name] = { + "status": "error", + "error": stderr or f"exit code {result.returncode}", + "stashed": did_stash, + } + except Exception as e: + results[pkg.name] = {"status": "error", "error": str(e)} + return results
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_dev/_sync/_tags.html b/src/scitex/_sphinx_html/_modules/scitex_dev/_sync/_tags.html new file mode 100644 index 000000000..ac49678a9 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_dev/_sync/_tags.html @@ -0,0 +1,780 @@ + + + + + + + + scitex_dev._sync._tags — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_dev._sync._tags

+#!/usr/bin/env python3
+# Timestamp: 2026-04-27
+# File: scitex_dev/sync_tags.py
+
+"""Push local git tags for ecosystem packages to origin.
+
+Extracted from ``sync.py`` to keep that module under the line-limit.
+``scitex_dev.sync`` re-exports ``sync_tags`` for backward compat.
+"""
+
+from __future__ import annotations
+
+import subprocess
+from pathlib import Path
+from typing import Any
+
+from .._core.config import DevConfig, load_config
+
+
+
+[docs] +def sync_tags( + packages: list[str] | None = None, + confirm: bool = False, + config: DevConfig | None = None, +) -> dict[str, Any]: + """Push local tags for all packages to origin. + + Safety: defaults to preview only. Pass confirm=True to execute. + + Parameters + ---------- + packages : list[str] | None + Package names. None = all configured packages. + confirm : bool + If False (default), preview only. + If True, execute git push --tags. + config : DevConfig | None + Configuration. + + Returns + ------- + dict + {package: {status, tag, output|commands}}. + """ + if config is None: + config = load_config() + + targets = config.packages + if packages: + targets = [p for p in targets if p.name in packages] + + results: dict[str, Any] = {} + for pkg in targets: + if not pkg.local_path: + continue + + path = Path(pkg.local_path).expanduser() + if not path.exists(): + results[pkg.name] = {"status": "skipped", "error": f"{path} not found"} + continue + + try: + tag_result = subprocess.run( + ["git", "describe", "--tags", "--abbrev=0"], + capture_output=True, + text=True, + cwd=str(path), + timeout=10, + ) + tag = tag_result.stdout.strip() if tag_result.returncode == 0 else None + except Exception: + tag = None + + if not confirm: + results[pkg.name] = { + "status": "dry_run", + "tag": tag, + "commands": ["git", "push", "origin", "--tags"], + } + continue + + try: + push_result = subprocess.run( + ["git", "push", "origin", "--tags"], + capture_output=True, + text=True, + cwd=str(path), + timeout=30, + ) + if push_result.returncode == 0: + results[pkg.name] = { + "status": "ok", + "tag": tag, + "output": push_result.stderr.strip(), + } + else: + results[pkg.name] = { + "status": "error", + "tag": tag, + "error": push_result.stderr.strip(), + } + except Exception as e: + results[pkg.name] = {"status": "error", "error": str(e)} + return results
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_etc/_grid.html b/src/scitex/_sphinx_html/_modules/scitex_etc/_grid.html new file mode 100644 index 000000000..ecec4387c --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_etc/_grid.html @@ -0,0 +1,740 @@ + + + + + + + + scitex_etc._grid — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_etc._grid

+#!/usr/bin/env python3
+# File: src/scitex_etc/_grid.py
+
+"""Combinatorial parameter-grid iteration helpers.
+
+Generic utilities for parameter sweeps: count the number of combinations in a
+grid and yield each combination as a dict. No domain ties — pure stdlib.
+"""
+
+from __future__ import annotations
+
+import itertools as _itertools
+import random as _random
+
+
+
+[docs] +def yield_grids(params_grid: dict, random: bool = False): + """Yield every parameter combination from a grid as a dict. + + Parameters + ---------- + params_grid : dict + Mapping of parameter name -> list of candidate values. + random : bool, optional + If True, yield the combinations in shuffled order (default False). + + Yields + ------ + dict + One combination, mapping each parameter name to a single value. + + Example + ------- + >>> grid = {"a": [1, 2], "b": ["x", "y"]} + >>> list(yield_grids(grid)) + [{'a': 1, 'b': 'x'}, {'a': 1, 'b': 'y'}, {'a': 2, 'b': 'x'}, {'a': 2, 'b': 'y'}] + """ + combinations = list(_itertools.product(*params_grid.values())) + if random: + _random.shuffle(combinations) + for values in combinations: + yield dict(zip(params_grid.keys(), values))
+ + + +
+[docs] +def count_grids(params_grid: dict) -> int: + """Return the total number of combinations in a parameter grid. + + Parameters + ---------- + params_grid : dict + Mapping of parameter name -> list of candidate values. + + Returns + ------- + int + Product of the lengths of all value lists. + """ + num_combinations = 1 + for values in params_grid.values(): + num_combinations *= len(values) + return num_combinations
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_etc/_search.html b/src/scitex/_sphinx_html/_modules/scitex_etc/_search.html new file mode 100644 index 000000000..219de3573 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_etc/_search.html @@ -0,0 +1,813 @@ + + + + + + + + scitex_etc._search — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_etc._search

+#!/usr/bin/env python3
+# File: src/scitex_etc/_search.py
+
+"""Regex pattern search over collections of strings.
+
+Generic cross-cutting helper. Stdlib-only at runtime: numpy, pandas, xarray and
+natsort are all *optional* — when present, their array/series types are accepted
+as inputs and natural sort ordering is used; otherwise plain lists and the
+builtin ``sorted`` are used.
+"""
+
+from __future__ import annotations
+
+import re
+from collections import abc
+
+try:  # optional — natural sort ordering when available
+    from natsort import natsorted as _natsorted
+except ImportError:  # pragma: no cover — fallback path
+
+    def _natsorted(iterable):
+        return sorted(iterable)
+
+
+def _to_list(string_or_pattern):
+    """Coerce assorted string containers into a plain list of strings."""
+    # numpy arrays (optional dependency)
+    try:
+        import numpy as _np
+
+        if isinstance(string_or_pattern, _np.ndarray):
+            return string_or_pattern.tolist()
+    except ImportError:
+        pass
+
+    # pandas Series / Index (optional dependency)
+    try:
+        import pandas as _pd
+
+        if isinstance(string_or_pattern, (_pd.Series, _pd.Index)):
+            return string_or_pattern.tolist()
+    except ImportError:
+        pass
+
+    # xarray DataArray (optional, lazy to avoid atexit hang issues)
+    try:
+        import xarray as _xr
+
+        if isinstance(string_or_pattern, _xr.DataArray):
+            return string_or_pattern.tolist()
+    except ImportError:
+        pass
+
+    if isinstance(string_or_pattern, abc.KeysView):
+        return list(string_or_pattern)
+    if not isinstance(string_or_pattern, (list, tuple)):
+        return [string_or_pattern]
+    return string_or_pattern
+
+
+
+
+
+
+# EOF
+
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_etc/wait_key.html b/src/scitex/_sphinx_html/_modules/scitex_etc/wait_key.html new file mode 100644 index 000000000..ae7f8c5f3 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_etc/wait_key.html @@ -0,0 +1,736 @@ + + + + + + + + scitex_etc.wait_key — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_etc.wait_key

+#!/usr/bin/env python3
+# File: src/scitex_etc/wait_key.py
+
+import multiprocessing
+import time
+
+import readchar
+
+
+def wait_key(p, *, read_key=None, printer=print):
+    """Block until the user presses ``q``, then terminate process ``p``.
+
+    Echoes each key as it is pressed; on ``q`` prints a notice and calls
+    ``p.terminate()``.
+
+    Parameters
+    ----------
+    p
+        A process-like object exposing ``terminate()``.
+    read_key : callable, optional
+        Zero-arg callable returning the next key as a string. Defaults to
+        ``readchar.readchar``. Injectable as a test seam (no mocks).
+    printer : callable, optional
+        Output sink, defaults to the builtin ``print``. Injectable so tests
+        can record output without patching ``builtins.print``.
+    """
+    if read_key is None:
+        read_key = readchar.readchar
+    key = "x"
+    while key != "q":
+        key = read_key()
+        printer(key)
+    printer("q was pressed.")
+    p.terminate()
+
+
+
+[docs] +def count(*, printer=print, sleeper=time.sleep): + """Print an incrementing counter forever, sleeping 1s between values. + + Parameters + ---------- + printer : callable, optional + Output sink, defaults to the builtin ``print``. Injectable test seam. + sleeper : callable, optional + One-arg sleep callable, defaults to ``time.sleep``. Injectable so + tests can run without real delay and bound the loop. + """ + counter = 0 + while True: + printer(counter) + sleeper(1) + counter += 1
+ + + +if __name__ == "__main__": + p1 = multiprocessing.Process(target=count) + + p1.start() + wait_key(p1) + print("aaa") + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_gists/_SigMacro_processFigure_S.html b/src/scitex/_sphinx_html/_modules/scitex_gists/_SigMacro_processFigure_S.html new file mode 100644 index 000000000..68b7ec425 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_gists/_SigMacro_processFigure_S.html @@ -0,0 +1,805 @@ + + + + + + + + scitex_gists._SigMacro_processFigure_S — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for scitex_gists._SigMacro_processFigure_S

+
+[docs] +def sigmacro_process_figure_s(): + """Print a macro for SigmaPlot (v12.0) to format a panel. + + Please refer to the 'Automating Routine Tasks' section of the official documentation. + """ + print( + """ +Option Explicit + +' Constants for FLAG_SET_BIT and FLAG_CLEAR_BIT should be defined +Const FLAG_SET_BIT As Long = 1 ' Assuming value, replace with actual value +Const FLAG_CLEAR_BIT As Long = 0 ' Assuming value, replace with actual value + +' Function to set option flag bits on +Function FlagOn(flag As Long) As Long + FlagOn = flag Or FLAG_SET_BIT +End Function + +' Function to set option flag bits off +Function FlagOff(flag As Long) As Long + FlagOff = flag And Not FLAG_CLEAR_BIT +End Function + +' Procedure to set the title size to 8 points +Sub setTitleSize() + ActiveDocument.CurrentPageItem.GraphPages(0).CurrentPageObject(GPT_GRAPH).NameObject.SetObjectCurrent + ActiveDocument.CurrentPageItem.SetCurrentObjectAttribute(GPM_SETOBJECTATTR, STA_SIZE, "111") ' Size set to 8 points +End Sub + +' Procedure to set label size for a given dimension to 8 points +Sub setLabelSize(dimension) + ' ActiveDocument.CurrentPageItem.GraphPages(0).CurrentPageObject(GPT_GRAPH).NameObject.SetObjectCurrent + ActiveDocument.CurrentPageItem.GraphPages(0).CurrentPageObject(GPT_AXIS).NameObject.SetObjectCurrent + ActiveDocument.CurrentPageItem.SetCurrentObjectAttribute(GPM_SETPLOTATTR, SLA_SELECTDIM, dimension) + ActiveDocument.CurrentPageItem.SetCurrentObjectAttribute(GPM_SETOBJECTATTR, STA_SIZE, "111") ' Size set to 8 points +End Sub + +' Procedure to set tick label size for a given dimension to 7 points +Sub setTickLabelSize(dimension) + ActiveDocument.CurrentPageItem.GraphPages(0).CurrentPageObject(GPT_GRAPH).NameObject.SetObjectCurrent + ActiveDocument.CurrentPageItem.SetCurrentObjectAttribute(GPM_SETPLOTATTR, SLA_SELECTDIM, dimension) + ActiveDocument.CurrentPageItem.GraphPages(0).CurrentPageObject(GPT_AXIS).TickLabelAttributes(SAA_LINE_MAJORTIC).SetObjectCurrent + ActiveDocument.CurrentPageItem.SetCurrentObjectAttribute(GPM_SETOBJECTATTR, STA_SIZE, "97") ' Size set to 7 points +End Sub + +' Procedure to process tick settings for a given dimension +Sub processTicks(dimension) + ' Ensure the object is correctly targeted before setting attributes + ActiveDocument.CurrentPageItem.GraphPages(0).CurrentPageObject(GPT_AXIS).NameObject.SetObjectCurrent + ActiveDocument.CurrentPageItem.SetCurrentObjectAttribute(GPM_SETPLOTATTR, SLA_SELECTDIM, dimension) + ActiveDocument.CurrentPageItem.SetCurrentObjectAttribute(GPM_SETAXISATTR, SAA_SELECTLINE, 1) + ActiveDocument.CurrentPageItem.SetCurrentObjectAttribute(GPM_SETAXISATTR, SEA_THICKNESS, &H00000008) + ActiveDocument.CurrentPageItem.SetCurrentObjectAttribute(GPM_SETAXISATTR, SAA_TICSIZE, &H00000020) + ActiveDocument.CurrentPageItem.SetCurrentObjectAttribute(GPM_SETAXISATTR, SAA_SELECTLINE, 2) + ActiveDocument.CurrentPageItem.SetCurrentObjectAttribute(GPM_SETAXISATTR, SEA_THICKNESS, &H00000008) + ActiveDocument.CurrentPageItem.SetCurrentObjectAttribute(GPM_SETAXISATTR, SAA_TICSIZE, &H00000020) +End Sub + +' Procedure to remove an axis for a given dimension +Sub removeAxis(dimension) + ActiveDocument.CurrentPageItem.SetCurrentObjectAttribute(GPM_SETPLOTATTR, SLA_SELECTDIM, dimension) + ActiveDocument.CurrentPageItem.SetCurrentObjectAttribute(GPM_SETAXISATTR, SAA_SUB2OPTIONS, &H00000000) +End Sub + +Sub resizeFigure + ActiveDocument.CurrentPageItem.GraphPages(0).CurrentPageObject(GPT_GRAPH).NameObject.SetObjectCurrent + With ActiveDocument.CurrentPageItem.GraphPages(0).CurrentPageObject(GPT_GRAPH) + '.Top = 0 + '.Left = 0 + .Width = &H000004F5& + .Height = &H00000378& + End With +End Sub + +' Main procedure +Sub Main() + Dim FullPATH As String + Dim OrigPageName As String + Dim ObjectType As String + Dim COLOR As Long + + ' Saves the original page to jump back + FullPATH = ActiveDocument.FullName + OrigPageName = ActiveDocument.CurrentPageItem.Name + ActiveDocument.NotebookItems(OrigPageName).IsCurrentBrowserEntry = True + + ' Code to set the figure size should be implemented here + resizeFigure + + ' Set the title sizes + setTitleSize + + ' Set the sizes of X/Y labels + setLabelSize(1) ' X-axis + setLabelSize(2) ' Y-axis + + ' Set the sizes of X/Y tick labels + setTickLabelSize(1) ' X-axis + setTickLabelSize(2) ' Y-axis + + ' Set tick length and width + processTicks(1) ' X-axis + processTicks(2) ' Y-axis + + ' Remove right and top axes + removeAxis(1) ' Right axis + removeAxis(2) ' Top axis + + ' Go back to the original page + Notebooks(FullPATH).NotebookItems(OrigPageName).Open + +End Sub + """ + )
+ + + +# Backward compatibility alias +import warnings + + +
+[docs] +def SigMacro_processFigure_S(): + """Deprecated: Use sigmacro_process_figure_s() instead.""" + warnings.warn( + "SigMacro_processFigure_S is deprecated, use sigmacro_process_figure_s() instead", + DeprecationWarning, + stacklevel=2, + ) + return sigmacro_process_figure_s()
+ +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_gists/_SigMacro_toBlue.html b/src/scitex/_sphinx_html/_modules/scitex_gists/_SigMacro_toBlue.html new file mode 100644 index 000000000..a4049c958 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_gists/_SigMacro_toBlue.html @@ -0,0 +1,849 @@ + + + + + + + + scitex_gists._SigMacro_toBlue — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for scitex_gists._SigMacro_toBlue

+
+[docs] +def sigmacro_to_blue(): + """Print a macro for SigmaPlot (v12.0) that changes the color and style of the selected object. + + Please refer to the 'Automating Routine Tasks' section of the official documentation. + """ + print( + """ +Option Explicit + +Function FlagOn(flag As Long) As Long + FlagOn = flag Or FLAG_SET_BIT ' Use to set option flag bits on, leaving others unchanged +End Function + +Function FlagOff(flag As Long) As Long + FlagOff = flag Or FLAG_CLEAR_BIT ' Use to set option flag bits off, leaving others unchanged +End Function + +Function getColor(colorName As String) As Long + Select Case colorName + + Case "Black" + getColor = RGB(0, 0, 0) + Case "Gray" + getColor = RGB(128, 128, 128) + Case "White" + getColor = RGB(255, 255, 255) + + + Case "Blue" + getColor = RGB(0, 128, 192) + Case "Green" + getColor = RGB(20, 180, 20) + Case "Red" + getColor = RGB(255, 70, 50) + + + Case "Yellow" + getColor = RGB(230, 160, 20) + Case "Purple" + getColor = RGB(200, 50, 255) + + + Case "Pink" + getColor = RGB(255, 150, 200) + Case "LightBlue" + getColor = RGB(20, 200, 200) + + + Case "DarkBlue" + getColor = RGB(0, 0, 100) + Case "Dan" + getColor = RGB(228, 94, 50) + Case "Brown" + getColor = RGB(128, 0, 0) + + Case Else + ' Default or error handling + getColor = RGB(0, 0, 0) ' Returning black as default or for an unrecognized color + End Select +End Function + + +Sub updatePlot(COLOR As Long) + ActiveDocument.CurrentPageItem.GraphPages(0).CurrentPageObject(GPT_GRAPH).NameObject.SetObjectCurrent + ActiveDocument.CurrentPageItem.SetCurrentObjectAttribute(GPM_SETPLOTATTR, SEA_COLOR, COLOR) + ActiveDocument.CurrentPageItem.SetCurrentObjectAttribute(GPM_SETPLOTATTR, SEA_COLORREPEAT, &H00000002&) + ActiveDocument.CurrentPageItem.SetCurrentObjectAttribute(GPM_SETPLOTATTR, SEA_THICKNESS, &H00000005&) ' .12 mm = .047 Inches +End Sub + +Sub updateScatter(COLOR As Long) + ActiveDocument.CurrentPageItem.GraphPages(0).CurrentPageObject(GPT_GRAPH).NameObject.SetObjectCurrent + ActiveDocument.CurrentPageItem.SetCurrentObjectAttribute(GPM_SETPLOTATTR, SSA_EDGECOLOR, COLOR) + ActiveDocument.CurrentPageItem.SetCurrentObjectAttribute(GPM_SETPLOTATTR, SSA_COLOR, COLOR) + ActiveDocument.CurrentPageItem.SetCurrentObjectAttribute(GPM_SETPLOTATTR, SSA_COLORREPEAT, &H00000002&) + ActiveDocument.CurrentPageItem.SetCurrentObjectAttribute(GPM_SETPLOTATTR, SSA_SIZE, &H00000020&) ' .8 mm = .032 inch + ' fixme scattersize=0.032 Innches +End Sub +Sub updateSolid(COLOR As Long) + ActiveDocument.CurrentPageItem.GraphPages(0).CurrentPageObject(GPT_GRAPH).NameObject.SetObjectCurrent + ActiveDocument.CurrentPageItem.SetCurrentObjectAttribute(GPM_SETPLOTATTR, SDA_COLOR, COLOR) + ActiveDocument.CurrentPageItem.SetCurrentObjectAttribute(GPM_SETPLOTATTR, SDA_COLORREPEAT, &H00000002&) +End Sub + + + +Function findObjectType() As String + On Error GoTo ErrorHandler + + Dim ObjectType As Variant + Dim object_type As Variant + object_type = ActiveDocument.CurrentPageItem.GraphPages(0).CurrentPageObject(GPT_GRAPH).Plots(0).GetAttribute(SLA_TYPE, ObjectType) + + If object_type = False Then + findObjectType = "Error: Failed to get object type." + Exit Function + End If + + ' Map the ObjectType to a string + Select Case object_type + Case SLA_TYPE_SCATTER + findObjectType = "Scatter/Line" + Case SLA_TYPE_BAR + findObjectType = "Bar" + Case SLA_TYPE_STACKED + findObjectType = "Stacked Bar" + Case SLA_TYPE_TUKEY + findObjectType = "Box" + Case SLA_TYPE_3DSCATTER + findObjectType = "3D Scatter/Line" + Case Else + findObjectType = "Unknown Object Type: " & object_type + End Select + Exit Function + +ErrorHandler: + findObjectType = "An error has occurred: " & Err.Description +End Function + +Sub Main() + On Error GoTo ErrorHandler + + Dim FullPATH As String + Dim OrigPageName As String + Dim ObjectType As String + Dim COLOR As Long + + ' Remember the original page + FullPATH = ActiveDocument.FullName + OrigPageName = ActiveDocument.CurrentPageItem.Name + ActiveDocument.NotebookItems(OrigPageName).IsCurrentBrowserEntry = True + + ' Get the color value for blue + COLOR = getColor("Blue") + + ' Find the type of the object + ObjectType = findObjectType() + + ' Check the object type and call the corresponding update function + If ObjectType = "Scatter/Line" Or ObjectType = "3D Scatter/Line" Then + updatePlot COLOR + updateScatter COLOR + ElseIf ObjectType = "Bar" Or ObjectType = "Stacked Bar" Or ObjectType = "Box" Then + updateSolid COLOR + Else + ' Raise a custom error + Err.Raise vbObjectError + 513, "Main", "Unknown or unsupported object type: " & ObjectType + End If + + ' Go back to the original page + Notebooks(FullPATH).NotebookItems(OrigPageName).Open + + Exit Sub + +ErrorHandler: + MsgBox "An error has occurred: " & Err.Description +End Sub +""" + )
+ + + +# Backward compatibility alias +import warnings + + +
+[docs] +def SigMacro_toBlue(): + """Deprecated: Use sigmacro_to_blue() instead.""" + warnings.warn( + "SigMacro_toBlue is deprecated, use sigmacro_to_blue() instead", + DeprecationWarning, + stacklevel=2, + ) + return sigmacro_to_blue()
+ +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_logging/_Tee.html b/src/scitex/_sphinx_html/_modules/scitex_logging/_Tee.html new file mode 100644 index 000000000..3e676519b --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_logging/_Tee.html @@ -0,0 +1,873 @@ + + + + + + + + scitex_logging._Tee — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_logging._Tee

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Timestamp: "2025-10-13 07:12:49 (ywatanabe)"
+# File: /home/ywatanabe/proj/scitex_repo/src/scitex/logging/_Tee.py
+# ----------------------------------------
+from __future__ import annotations
+
+import os
+
+__FILE__ = __file__
+__DIR__ = os.path.dirname(__FILE__)
+# ----------------------------------------
+
+THIS_FILE = __file__
+
+"""
+Functionality:
+    * Redirects and logs standard output and error streams
+    * Filters progress bar outputs from stderr logging
+    * Maintains original stdout/stderr functionality while logging
+Input:
+    * System stdout/stderr streams
+    * Output file paths for logging
+Output:
+    * Wrapped stdout/stderr objects with logging capability
+    * Log files containing stdout and stderr outputs
+Prerequisites:
+    * Python 3.6+
+    * scitex package for path handling and colored printing
+"""
+
+"""Imports"""
+import os as _os
+import re
+import sys
+from typing import Any, TextIO
+
+
+def _clean_path(path_string):
+    """Normalize a file system path (stdlib replacement for scitex.str.clean_path)."""
+    if hasattr(path_string, "__fspath__"):
+        path_string = str(path_string)
+    return _os.path.normpath(path_string)
+
+
+"""Functions & Classes"""
+
+
+def _get_logger():
+    """Get logger lazily to avoid circular import during module initialization."""
+    import logging
+
+    return logging.getLogger(__name__)
+
+
+
+[docs] +class Tee: + def __init__(self, stream: TextIO, log_path: str, verbose=True) -> None: + self.verbose = verbose + self._stream = stream + self._log_path = log_path + try: + self._log_file = open(log_path, "w", buffering=1) # Line buffering + if verbose: + # Show where logs are being saved using scitex logging + logger = _get_logger() + stream_name = "stderr" if stream is sys.stderr else "stdout" + logger.debug(f"Tee [{stream_name}]: {log_path}") + except Exception as e: + print(f"Failed to open log file {log_path}: {e}", file=sys.stderr) + self._log_file = None + self._is_stderr = stream is sys.stderr + +
+[docs] + def write(self, data: Any) -> None: + self._stream.write(data) + if self._log_file is not None: + if self._is_stderr: + if isinstance(data, str) and not re.match( + r"^[\s]*[0-9]+%.*\[A*$", data + ): + self._log_file.write(data) + self._log_file.flush() # Ensure immediate write + else: + self._log_file.write(data) + self._log_file.flush() # Ensure immediate write
+ + +
+[docs] + def flush(self) -> None: + self._stream.flush() + if self._log_file is not None: + self._log_file.flush()
+ + +
+[docs] + def isatty(self) -> bool: + return self._stream.isatty()
+ + +
+[docs] + def fileno(self) -> int: + return self._stream.fileno()
+ + + @property + def buffer(self): + return self._stream.buffer + +
+[docs] + def close(self): + """Explicitly close the log file.""" + if hasattr(self, "_log_file") and self._log_file is not None: + try: + self._log_file.flush() + self._log_file.close() + if self.verbose: + # Use lazy logger to avoid circular import + logger = _get_logger() + logger.debug(f"Tee: Closed log file: {self._log_path}") + self._log_file = None # Prevent double-close + except Exception: + pass
+ + + def __del__(self): + # Only attempt cleanup if Python is not shutting down + # This prevents "Exception ignored" errors during interpreter shutdown + if hasattr(self, "_log_file") and self._log_file is not None: + try: + # Check if the file object is still valid + if hasattr(self._log_file, "closed") and not self._log_file.closed: + self.close() + except Exception: + # Silently ignore exceptions during cleanup + pass
+ + + +
+[docs] +def tee(sys, sdir=None, verbose=True): + """Redirects stdout and stderr to both console and log files. + + Example + ------- + >>> import sys + >>> sys.stdout, sys.stderr = tee(sys) + >>> print("abc") # stdout + >>> print(1 / 0) # stderr + + Parameters + ---------- + sys_module : module + System module containing stdout and stderr + sdir : str, optional + Directory for log files + verbose : bool, default=True + Whether to print log file locations + + Returns + ------- + tuple[Any, Any] + Wrapped stdout and stderr objects + """ + import inspect + + #################### + ## Determine sdir + ## DO NOT MODIFY THIS + #################### + if sdir is None: + THIS_FILE = inspect.stack()[1].filename + if "ipython" in THIS_FILE: + THIS_FILE = f"/tmp/{_os.getenv('USER')}.py" + sdir = _clean_path(_os.path.splitext(THIS_FILE)[0] + "_out") + + sdir = _os.path.join(sdir, "logs/") + _os.makedirs(sdir, exist_ok=True) + + spath_stdout = sdir + "stdout.log" + spath_stderr = sdir + "stderr.log" + sys_stdout = Tee(sys.stdout, spath_stdout) + sys_stderr = Tee(sys.stderr, spath_stderr) + + if verbose: + message = f"Standard output/error are being logged at:\n\t{spath_stdout}\n\t{spath_stderr}" + logger = _get_logger() + logger.info(message) + # printc(message) + + return sys_stdout, sys_stderr
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_logging/_config.html b/src/scitex/_sphinx_html/_modules/scitex_logging/_config.html new file mode 100644 index 000000000..0587a68fc --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_logging/_config.html @@ -0,0 +1,851 @@ + + + + + + + + scitex_logging._config — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_logging._config

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Timestamp: "2025-08-21 21:41:37 (ywatanabe)"
+# File: /home/ywatanabe/proj/scitex_repo/src/scitex/log/_config.py
+# ----------------------------------------
+from __future__ import annotations
+
+import os
+
+__FILE__ = __file__
+__DIR__ = os.path.dirname(__FILE__)
+# ----------------------------------------
+
+"""Configuration and setup for SciTeX logging."""
+
+import logging
+from typing import Optional, Union
+
+from ._handlers import create_console_handler, create_file_handler, get_default_log_path
+from ._levels import CRITICAL, DEBUG, ERROR, FAIL, INFO, SUCCESS, WARNING
+from ._logger import setup_logger_class
+
+# Global level variable
+_GLOBAL_LEVEL = None
+_FILE_LOGGING_ENABLED = True  # Enable file logging by default
+
+
+
+[docs] +def set_level(level: Union[str, int]): + """Set global log level for all SciTeX loggers.""" + global _GLOBAL_LEVEL + + level_map = { + "debug": DEBUG, + "info": INFO, + "warning": WARNING, + "error": ERROR, + "critical": CRITICAL, + "success": SUCCESS, + "fail": FAIL, + } + + if isinstance(level, str): + level = level_map.get(level.lower(), level) + + _GLOBAL_LEVEL = level + logging.getLogger().setLevel(level) + + # Update all existing handlers + for handler in logging.getLogger().handlers: + handler.setLevel(level)
+ + + +
+[docs] +def get_level(): + """Get current global log level.""" + return _GLOBAL_LEVEL or logging.getLogger().level
+ + + +
+[docs] +def enable_file_logging(enabled: bool = True): + """Enable or disable file logging globally.""" + global _FILE_LOGGING_ENABLED + _FILE_LOGGING_ENABLED = enabled
+ + + +
+[docs] +def is_file_logging_enabled(): + """Check if file logging is enabled.""" + return _FILE_LOGGING_ENABLED
+ + + +
+[docs] +def configure( + level: Union[str, int] = "info", + log_file: Optional[str] = None, + enable_file: bool = True, + enable_console: bool = True, + capture_prints: bool = True, + max_file_size: int = 10 * 1024 * 1024, + backup_count: int = 5, +): + """Configure logging for SciTeX with both console and file output. + + Args: + level: Log level (string or logging constant) + log_file: Path to log file (default: ~/.scitex/logs/scitex-YYYY-MM-DD.log) + enable_file: Whether to enable file logging + enable_console: Whether to enable console logging + capture_prints: Whether to capture print() statements to logs + max_file_size: Maximum size of log file before rotation (default: 10MB) + backup_count: Number of backup files to keep (default: 5) + """ + # Setup custom logger class + setup_logger_class() + + # Convert level if string + level_map = { + "debug": DEBUG, + "info": INFO, + "warning": WARNING, + "error": ERROR, + "critical": CRITICAL, + "success": SUCCESS, + "fail": FAIL, + } + + if isinstance(level, str): + level = level_map.get(level.lower(), level) + + # Set global level + set_level(level) + + # Clear any existing handlers + root_logger = logging.getLogger() + for handler in root_logger.handlers[:]: + root_logger.removeHandler(handler) + + handlers = [] + + # Add console handler if enabled + if enable_console: + console_handler = create_console_handler(level) + handlers.append(console_handler) + + # Add file handler if enabled + if enable_file and is_file_logging_enabled(): + if log_file is None: + log_file = get_default_log_path() + + file_handler = create_file_handler( + log_file, level, max_bytes=max_file_size, backup_count=backup_count + ) + handlers.append(file_handler) + + # Configure basic logging with our handlers + logging.basicConfig( + level=level, + handlers=handlers, + force=True, # Force reconfiguration + ) + + # Enable print capture if requested + if capture_prints: + from ._print_capture import enable_print_capture + + enable_print_capture()
+ + + +
+[docs] +def get_log_path(): + """Get the current log file path.""" + for handler in logging.getLogger().handlers: + if hasattr(handler, "baseFilename"): + return handler.baseFilename + return None
+ + + +__all__ = [ + "set_level", + "get_level", + "enable_file_logging", + "is_file_logging_enabled", + "configure", + "get_log_path", +] + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_logging/_context.html b/src/scitex/_sphinx_html/_modules/scitex_logging/_context.html new file mode 100644 index 000000000..9c94103dc --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_logging/_context.html @@ -0,0 +1,779 @@ + + + + + + + + scitex_logging._context — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_logging._context

+#!/usr/bin/env python3
+# Timestamp: "2025-10-11 22:30:00 (ywatanabe)"
+# File: /home/ywatanabe/proj/scitex_repo/src/scitex/logging/_context.py
+# ----------------------------------------
+from __future__ import annotations
+
+import os
+
+__FILE__ = "./src/scitex/logging/_context.py"
+__DIR__ = os.path.dirname(__FILE__)
+# ----------------------------------------
+
+"""
+Context manager for temporary logging to specific files.
+
+Provides clean API for adding/removing file handlers during execution.
+"""
+
+import logging as _logging
+from contextlib import contextmanager
+from pathlib import Path
+from typing import Optional, Union
+
+from ._formatters import SciTeXFileFormatter
+
+
+
+[docs] +@contextmanager +def log_to_file( + file_path: Union[str, Path], + level: int = _logging.DEBUG, + mode: str = "w", + formatter: Optional[_logging.Formatter] = None, +): + """Context manager to temporarily log all output to a specific file. + + Usage: + import scitex_logging as logging + logger = logging.getLogger(__name__) + + with logging.log_to_file("/path/to/log.txt"): + logger.info("This goes to both console and /path/to/log.txt") + logger.success("This too!") + + Args: + file_path: Path to log file + level: Logging level for this handler (default: DEBUG) + mode: File mode ('w' for overwrite, 'a' for append) + formatter: Custom formatter (default: SciTeXFileFormatter) + + Yields + ------ + The file handler (can be ignored) + """ + # Ensure directory exists + file_path = Path(file_path) + file_path.parent.mkdir(parents=True, exist_ok=True) + + # Create handler + handler = _logging.FileHandler(str(file_path), mode=mode) + handler.setLevel(level) + + # Set formatter + if formatter is None: + formatter = SciTeXFileFormatter() + handler.setFormatter(formatter) + + # Add to root logger + root_logger = _logging.getLogger() + root_logger.addHandler(handler) + + # Log where output is going (lazy import to avoid circular dependency) + def _log_info(): + try: + import scitex_logging as logging + + logger = logging.getLogger(__name__) + logger.debug(f"Logging to: {file_path}") + except: + pass # Silently fail if logging not ready + + _log_info() + + try: + yield handler + finally: + # Clean up handler + root_logger.removeHandler(handler) + handler.close() + + # Log completion (lazy import) + def _log_saved(): + try: + import scitex_logging as logging + + logger = logging.getLogger(__name__) + logger.debug(f"Log saved: {file_path}") + except: + pass + + _log_saved()
+ + + +__all__ = ["log_to_file"] + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_logging/_errors.html b/src/scitex/_sphinx_html/_modules/scitex_logging/_errors.html new file mode 100644 index 000000000..f67582dea --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_logging/_errors.html @@ -0,0 +1,1286 @@ + + + + + + + + scitex_logging._errors — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_logging._errors

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Timestamp: "2025-12-21"
+# File: /home/ywatanabe/proj/scitex-code/src/scitex/logging/_errors.py
+
+"""Error classes for SciTeX.
+
+All SciTeX exceptions are defined here for unified error handling.
+Re-exported from scitex.logging for backwards compatibility.
+
+Usage:
+    from scitex_logging import SciTeXError, SaveError, LoadError
+    # or (backwards compatible)
+    from scitex_logging import SciTeXError, SaveError, LoadError
+"""
+
+from typing import Optional, Union
+
+# =============================================================================
+# Base Errors
+# =============================================================================
+
+
+
+[docs] +class SciTeXError(Exception): + """Base Exception class for all SciTeX errors.""" + +
+[docs] + def __init__( + self, + message: str, + context: Optional[dict] = None, + suggestion: Optional[str] = None, + ): + """Initialize SciTeX error with detailed information. + + Parameters + ---------- + message : str + The error message + context : dict, optional + Additional context information (e.g., file paths, variable values) + suggestion : str, optional + Suggested fix or action + """ + self.message = message + self.context = context or {} + self.suggestion = suggestion + + # Build the full error message + error_parts = [f"SciTeX Error: {message}"] + + if context: + error_parts.append("\nContext:") + for key, value in context.items(): + error_parts.append(f" {key}: {value}") + + if suggestion: + error_parts.append(f"\nSuggestion: {suggestion}") + + super().__init__("\n".join(error_parts))
+
+ + + +# ============================================================================= +# Configuration Errors +# ============================================================================= + + +
+[docs] +class ConfigurationError(SciTeXError): + """Raised when there are issues with SciTeX configuration.""" + + pass
+ + + +
+[docs] +class ConfigFileNotFoundError(ConfigurationError): + """Raised when a required configuration file is not found.""" + + def __init__(self, filepath: str): + super().__init__( + f"Configuration file not found: {filepath}", + context={"filepath": filepath}, + suggestion="Ensure the configuration file exists in ./config/ directory", + )
+ + + +
+[docs] +class ConfigKeyError(ConfigurationError): + """Raised when a required configuration key is missing.""" + + def __init__(self, key: str, available_keys: Optional[list] = None): + context = {"missing_key": key} + if available_keys: + context["available_keys"] = available_keys + + super().__init__( + f"Configuration key '{key}' not found", + context=context, + suggestion=f"Add '{key}' to your configuration file or check for typos", + )
+ + + +# ============================================================================= +# IO Errors +# ============================================================================= + + +
+[docs] +class IOError(SciTeXError): + """Base class for input/output related errors.""" + + pass
+ + + +
+[docs] +class FileFormatError(IOError): + """Raised when file format is not supported or incorrect.""" + + def __init__( + self, + filepath: str, + expected_format: Optional[str] = None, + actual_format: Optional[str] = None, + ): + context = {"filepath": filepath} + if expected_format: + context["expected_format"] = expected_format + if actual_format: + context["actual_format"] = actual_format + + message = f"File format error for: {filepath}" + if expected_format and actual_format: + message += f" (expected: {expected_format}, got: {actual_format})" + + super().__init__( + message, + context=context, + suggestion="Check the file extension and content format", + )
+ + + +
+[docs] +class SaveError(IOError): + """Raised when saving data fails.""" + + def __init__(self, filepath: str, reason: str): + super().__init__( + f"Failed to save to {filepath}: {reason}", + context={"filepath": filepath, "reason": reason}, + suggestion="Check file permissions and disk space", + )
+ + + +
+[docs] +class LoadError(IOError): + """Raised when loading data fails.""" + + def __init__(self, filepath: str, reason: str): + super().__init__( + f"Failed to load from {filepath}: {reason}", + context={"filepath": filepath, "reason": reason}, + suggestion="Verify the file exists and is not corrupted", + )
+ + + +# ============================================================================= +# Scholar Module Errors +# ============================================================================= + + +
+[docs] +class ScholarError(SciTeXError): + """Base class for scholar module errors.""" + + pass
+ + + +
+[docs] +class SearchError(ScholarError): + """Raised when paper search fails.""" + + def __init__(self, query: str, source: str, reason: str): + super().__init__( + f"Search failed for query '{query}' on {source}", + context={"query": query, "source": source, "reason": reason}, + suggestion="Check your internet connection and API keys", + )
+ + + +
+[docs] +class EnrichmentError(ScholarError): + """Raised when paper enrichment fails.""" + + def __init__(self, paper_title: str, reason: str): + super().__init__( + f"Failed to enrich paper: {paper_title}", + context={"paper_title": paper_title, "reason": reason}, + suggestion="Verify journal information is available", + )
+ + + +
+[docs] +class PDFDownloadError(ScholarError): + """Raised when PDF download fails.""" + + def __init__(self, url: str, reason: str): + super().__init__( + f"Failed to download PDF from {url}", + context={"url": url, "reason": reason}, + suggestion="Check if the paper is open access", + )
+ + + +
+[docs] +class DOIResolutionError(ScholarError): + """Raised when DOI resolution fails.""" + + def __init__(self, doi: str, reason: str): + super().__init__( + f"Failed to resolve DOI: {doi}", + context={"doi": doi, "reason": reason}, + suggestion="Verify the DOI is correct and try again", + )
+ + + +
+[docs] +class PDFExtractionError(ScholarError): + """Raised when PDF text extraction fails.""" + + def __init__(self, filepath: str, reason: str): + super().__init__( + f"Failed to extract text from PDF: {filepath}", + context={"filepath": filepath, "reason": reason}, + suggestion="Ensure the PDF is not corrupted or encrypted", + )
+ + + +
+[docs] +class BibTeXEnrichmentError(ScholarError): + """Raised when BibTeX enrichment fails.""" + + def __init__(self, bibtex_file: str, reason: str): + super().__init__( + f"Failed to enrich BibTeX file: {bibtex_file}", + context={"bibtex_file": bibtex_file, "reason": reason}, + suggestion="Check the BibTeX format and ensure all entries are valid", + )
+ + + +
+[docs] +class TranslatorError(ScholarError): + """Raised when Zotero translator operations fail.""" + + def __init__(self, translator_name: str, reason: str): + super().__init__( + f"Translator error in {translator_name}: {reason}", + context={"translator": translator_name, "reason": reason}, + suggestion="Check translator compatibility and JavaScript environment", + )
+ + + +
+[docs] +class AuthenticationError(ScholarError): + """Raised when authentication fails.""" + + def __init__(self, provider: str, reason: str = ""): + super().__init__( + f"Authentication failed for {provider}: {reason}", + context={"provider": provider, "reason": reason}, + suggestion="Check your credentials and authentication settings", + )
+ + + +# ============================================================================= +# Plotting Errors +# ============================================================================= + + +
+[docs] +class PlottingError(SciTeXError): + """Base class for plotting-related errors.""" + + pass
+ + + +
+[docs] +class FigureNotFoundError(PlottingError): + """Raised when attempting to operate on a non-existent figure.""" + + def __init__(self, fig_id: Union[int, str]): + super().__init__( + f"Figure {fig_id} not found", + context={"figure_id": fig_id}, + suggestion="Ensure the figure was created before attempting to save/modify it", + )
+ + + +
+[docs] +class AxisError(PlottingError): + """Raised when there are issues with plot axes.""" + + def __init__(self, message: str, axis_info: Optional[dict] = None): + super().__init__( + message, + context={"axis_info": axis_info} if axis_info else None, + suggestion="Check axis indices and subplot configuration", + )
+ + + +# ============================================================================= +# Data Processing Errors +# ============================================================================= + + +
+[docs] +class DataError(SciTeXError): + """Base class for data processing errors.""" + + pass
+ + + +
+[docs] +class ShapeError(DataError): + """Raised when data shapes are incompatible.""" + + def __init__(self, expected_shape: tuple, actual_shape: tuple, operation: str): + super().__init__( + f"Shape mismatch in {operation}", + context={ + "expected_shape": expected_shape, + "actual_shape": actual_shape, + "operation": operation, + }, + suggestion="Reshape or transpose your data to match expected dimensions", + )
+ + + +
+[docs] +class DTypeError(DataError): + """Raised when data types are incompatible.""" + + def __init__(self, expected_dtype: str, actual_dtype: str, operation: str): + super().__init__( + f"Data type mismatch in {operation}", + context={ + "expected_dtype": expected_dtype, + "actual_dtype": actual_dtype, + "operation": operation, + }, + suggestion=f"Convert data to {expected_dtype} using appropriate casting", + )
+ + + +# ============================================================================= +# Path Errors +# ============================================================================= + + +
+[docs] +class PathError(SciTeXError): + """Base class for path-related errors.""" + + pass
+ + + +
+[docs] +class InvalidPathError(PathError): + """Raised when a path is invalid or doesn't follow SciTeX conventions.""" + + def __init__(self, path: str, reason: str): + super().__init__( + f"Invalid path: {path}", + context={"path": path, "reason": reason}, + suggestion="Use relative paths starting with './' or '../'", + )
+ + + +
+[docs] +class PathNotFoundError(PathError): + """Raised when a required path doesn't exist.""" + + def __init__(self, path: str): + super().__init__( + f"Path not found: {path}", + context={"path": path}, + suggestion="Check if the path exists and is accessible", + )
+ + + +# ============================================================================= +# Template Errors +# ============================================================================= + + +
+[docs] +class TemplateError(SciTeXError): + """Base class for template-related errors.""" + + pass
+ + + +
+[docs] +class TemplateViolationError(TemplateError): + """Raised when SciTeX template is not followed.""" + + def __init__(self, filepath: str, violation: str): + super().__init__( + f"Template violation in {filepath}: {violation}", + context={"filepath": filepath, "violation": violation}, + suggestion="Follow the SciTeX template structure as defined in IMPORTANT-SCITEX-02-file-template.md", + )
+ + + +# ============================================================================= +# Neural Network Errors +# ============================================================================= + + +
+[docs] +class NNError(SciTeXError): + """Base class for neural network module errors.""" + + pass
+ + + +
+[docs] +class ModelError(NNError): + """Raised when there are issues with neural network models.""" + + def __init__(self, model_name: str, reason: str): + super().__init__( + f"Model error in {model_name}: {reason}", + context={"model_name": model_name, "reason": reason}, + )
+ + + +# ============================================================================= +# Statistics Errors +# ============================================================================= + + +
+[docs] +class StatsError(SciTeXError): + """Base class for statistics module errors.""" + + pass
+ + + +
+[docs] +class TestError(StatsError): + """Raised when statistical tests fail.""" + + def __init__(self, test_name: str, reason: str): + super().__init__( + f"Statistical test '{test_name}' failed: {reason}", + context={"test_name": test_name, "reason": reason}, + )
+ + + +# ============================================================================= +# Validation Helpers +# ============================================================================= + + +
+[docs] +def check_path(path: str) -> None: + """Validate a path according to SciTeX conventions.""" + if not isinstance(path, str): + raise InvalidPathError(str(path), "Path must be a string") + + if not (path.startswith("./") or path.startswith("../")): + raise InvalidPathError( + path, "Path must be relative and start with './' or '../'" + )
+ + + +
+[docs] +def check_file_exists(filepath: str) -> None: + """Check if a file exists.""" + import os + + if not os.path.exists(filepath): + raise PathNotFoundError(filepath)
+ + + +
+[docs] +def check_shape_compatibility(shape1: tuple, shape2: tuple, operation: str) -> None: + """Check if two shapes are compatible for an operation.""" + if shape1 != shape2: + raise ShapeError(shape1, shape2, operation)
+ + + +__all__ = [ + # Base errors + "SciTeXError", + # Configuration + "ConfigurationError", + "ConfigFileNotFoundError", + "ConfigKeyError", + # IO + "IOError", + "FileFormatError", + "SaveError", + "LoadError", + # Scholar + "ScholarError", + "SearchError", + "EnrichmentError", + "PDFDownloadError", + "DOIResolutionError", + "PDFExtractionError", + "BibTeXEnrichmentError", + "TranslatorError", + "AuthenticationError", + # Plotting + "PlottingError", + "FigureNotFoundError", + "AxisError", + # Data + "DataError", + "ShapeError", + "DTypeError", + # Path + "PathError", + "InvalidPathError", + "PathNotFoundError", + # Template + "TemplateError", + "TemplateViolationError", + # Neural Network + "NNError", + "ModelError", + # Statistics + "StatsError", + "TestError", + # Validation helpers + "check_path", + "check_file_exists", + "check_shape_compatibility", +] + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_logging/_warnings.html b/src/scitex/_sphinx_html/_modules/scitex_logging/_warnings.html new file mode 100644 index 000000000..2ec539fd1 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_logging/_warnings.html @@ -0,0 +1,968 @@ + + + + + + + + scitex_logging._warnings — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_logging._warnings

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Timestamp: "2025-12-21"
+# File: /home/ywatanabe/proj/scitex-code/src/scitex/logging/_warnings.py
+
+"""Warning system for SciTeX, mimicking Python's warnings module.
+
+Usage:
+    import scitex_logging as logging
+    from scitex_logging import UnitWarning
+
+    # Emit a warning
+    logging.warn("Missing units on axis label", UnitWarning)
+
+    # Filter warnings (like warnings.filterwarnings)
+    logging.filterwarnings("ignore", category=UnitWarning)
+    logging.filterwarnings("error", category=UnitWarning)  # Raise as exception
+    logging.filterwarnings("always", category=UnitWarning)  # Always show
+"""
+
+import logging as _logging
+from typing import Dict, Optional, Set, Type
+
+# =============================================================================
+# Warning Categories (similar to Python's warning classes)
+# =============================================================================
+
+
+
+[docs] +class SciTeXWarning(UserWarning): + """Base warning class for all SciTeX warnings.""" + + pass
+ + + +
+[docs] +class UnitWarning(SciTeXWarning): + """Warning for axis label unit issues (educational for SI conventions). + + Raised when: + - Axis labels are missing units + - Units use parentheses instead of brackets (SI prefers []) + - Units use division instead of negative exponents (m/s vs m·s⁻¹) + """ + + pass
+ + + +
+[docs] +class StyleWarning(SciTeXWarning): + """Warning for style/formatting issues.""" + + pass
+ + + +
+[docs] +class SciTeXDeprecationWarning(SciTeXWarning): + """Warning for deprecated SciTeX features.""" + + pass
+ + + +
+[docs] +class PerformanceWarning(SciTeXWarning): + """Warning for performance issues.""" + + pass
+ + + +
+[docs] +class DataLossWarning(SciTeXWarning): + """Warning for potential data loss.""" + + pass
+ + + +# ============================================================================= +# Warning Filter Registry +# ============================================================================= + +# Actions: "ignore", "error", "always", "default", "once", "module" +_filters: Dict[Type[SciTeXWarning], str] = {} +_seen_warnings: Set[str] = set() # For "once" action + + +
+[docs] +def filterwarnings( + action: str, + category: Type[SciTeXWarning] = SciTeXWarning, + message: Optional[str] = None, +) -> None: + """Control warning behavior (like warnings.filterwarnings). + + Parameters + ---------- + action : str + One of: + - "ignore": Never show this warning + - "error": Raise as exception + - "always": Always show + - "default": Show first occurrence per location + - "once": Show only once total + - "module": Show once per module + category : type + Warning category (default: SciTeXWarning = all) + message : str, optional + Regex pattern to match warning message (not implemented yet) + + Examples + -------- + >>> import scitex.logging as logging + >>> from scitex.logging import UnitWarning + >>> logging.filterwarnings("ignore", category=UnitWarning) + """ + valid_actions = {"ignore", "error", "always", "default", "once", "module"} + if action not in valid_actions: + raise ValueError(f"Invalid action '{action}'. Must be one of: {valid_actions}") + + _filters[category] = action
+ + + +
+[docs] +def resetwarnings() -> None: + """Reset all warning filters to default behavior.""" + global _filters, _seen_warnings + _filters = {} + _seen_warnings = set()
+ + + +def _get_action(category: Type[SciTeXWarning]) -> str: + """Get the action for a warning category, checking inheritance.""" + # Check exact match first + if category in _filters: + return _filters[category] + + # Check parent classes + for filter_cat, action in _filters.items(): + if issubclass(category, filter_cat): + return action + + # Default action + return "default" + + +# ============================================================================= +# Warning Emission +# ============================================================================= + + +
+[docs] +def warn( + message: str, + category: Type[SciTeXWarning] = SciTeXWarning, + stacklevel: int = 2, +) -> None: + """Emit a warning (like warnings.warn but integrated with scitex.logging). + + Parameters + ---------- + message : str + Warning message + category : type + Warning category (default: SciTeXWarning) + stacklevel : int + Stack level for source location (default: 2 = caller) + + Examples + -------- + >>> import scitex.logging as logging + >>> from scitex.logging import UnitWarning + >>> logging.warn("X axis has no units", UnitWarning) + """ + import inspect + + action = _get_action(category) + + # Handle action + if action == "ignore": + return + + if action == "error": + raise category(message) + + # Get source location for "once", "module", "default" actions + frame = inspect.currentframe() + for _ in range(stacklevel): + if frame is not None: + frame = frame.f_back + + location = "" + if frame is not None: + filename = frame.f_code.co_filename + lineno = frame.f_lineno + location = f"{filename}:{lineno}" + + # Check if already seen + warn_key = f"{category.__name__}:{message}:{location}" + + if action == "once": + if warn_key in _seen_warnings: + return + _seen_warnings.add(warn_key) + elif action == "default": + # Show first per location + loc_key = f"{category.__name__}:{location}" + if loc_key in _seen_warnings: + return + _seen_warnings.add(loc_key) + elif action == "module": + # Show once per module + if frame is not None: + module_key = f"{category.__name__}:{frame.f_code.co_filename}" + if module_key in _seen_warnings: + return + _seen_warnings.add(module_key) + + # Emit via scitex.logging + logger = _logging.getLogger("scitex.warnings") + category_name = category.__name__ + + # Format: "UnitWarning: message" + logger.warning(f"{category_name}: {message}")
+ + + +# ============================================================================= +# Convenience Warning Functions +# ============================================================================= + + +
+[docs] +def warn_deprecated( + old_name: str, new_name: str, version: Optional[str] = None +) -> None: + """Issue a deprecation warning.""" + message = f"{old_name} is deprecated. Use {new_name} instead." + if version: + message += f" Will be removed in version {version}." + warn(message, SciTeXDeprecationWarning, stacklevel=3)
+ + + +
+[docs] +def warn_performance(operation: str, suggestion: str) -> None: + """Issue a performance warning.""" + message = f"Performance warning in {operation}: {suggestion}" + warn(message, PerformanceWarning, stacklevel=3)
+ + + +
+[docs] +def warn_data_loss(operation: str, detail: str) -> None: + """Issue a data loss warning.""" + message = f"Potential data loss in {operation}: {detail}" + warn(message, DataLossWarning, stacklevel=3)
+ + + +__all__ = [ + # Warning categories + "SciTeXWarning", + "UnitWarning", + "StyleWarning", + "SciTeXDeprecationWarning", + "PerformanceWarning", + "DataLossWarning", + # Functions + "warn", + "filterwarnings", + "resetwarnings", + # Convenience functions + "warn_deprecated", + "warn_performance", + "warn_data_loss", +] + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_msword.html b/src/scitex/_sphinx_html/_modules/scitex_msword.html new file mode 100644 index 000000000..706f25cc8 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_msword.html @@ -0,0 +1,989 @@ + + + + + + + + scitex_msword — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_msword

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Timestamp: 2025-12-11 15:15:00
+# File: /home/ywatanabe/proj/scitex-code/src/scitex/msword/__init__.py
+
+"""
+MS Word (DOCX) import/export utilities for SciTeX.
+
+This module provides high-level functions to convert between
+MS Word .docx files and SciTeX's internal writer document model.
+
+Strategy:
+---------
+- Word users write text only (paragraphs, minimal formatting)
+- SciTeX handles: figures, tables, references, LaTeX generation
+- SciTeX JSON is the "source of truth", Word is just a view/edit layer
+
+Typical usage:
+--------------
+    from scitex_msword import load_docx, save_docx, list_profiles
+
+    # Import from Word
+    doc = load_docx("input.docx", profile="generic")
+
+    # Manipulate via scitex.writer...
+    # doc.normalize()
+
+    # Export to Word (different journal template)
+    save_docx(doc, "output.docx", profile="mdpi-ijerph")
+
+Available profiles:
+-------------------
+- generic: Standard Word with Heading 1/2/3
+- mdpi-ijerph: MDPI IJERPH journal template
+- resna-2025: RESNA 2025 scientific paper template
+- iop-double-anonymous: IOP double-anonymous template
+"""
+
+from __future__ import annotations
+
+try:
+    from importlib.metadata import PackageNotFoundError
+    from importlib.metadata import version as _v
+
+    try:
+        __version__ = _v("scitex-msword")
+    except PackageNotFoundError:
+        __version__ = "0.0.0+local"
+    del _v, PackageNotFoundError
+except ImportError:  # pragma: no cover — only on ancient Pythons
+    __version__ = "0.0.0+local"
+
+from pathlib import Path
+from typing import Any, Optional
+
+from .bold import preserve_bold_tokens
+from .comments import apply_comments_as_edits, extract_comments
+from .diff import diff_docx, summarize_diff
+from .highlights import (
+    clear_highlights,
+    extract_highlights,
+    mark_additions,
+    mark_modifications,
+)
+from .profiles import BaseWordProfile, get_profile, list_profiles, register_profile
+from .reader import WordReader
+from .track_changes import (
+    accept_all_tracked_changes,
+    enable_track_changes,
+    extract_tracked_changes,
+    is_track_changes_enabled,
+    reject_all_tracked_changes,
+    wrap_as_tracked_deletion,
+    wrap_as_tracked_insertion,
+)
+from .utils import (
+    create_post_import_hook,
+    link_captions_to_images,
+    link_captions_to_images_by_proximity,
+    normalize_section_headings,
+    validate_document,
+)
+from .writer import WordWriter
+
+
+
+[docs] +def load_docx( + path: str | Path, + profile: str | None = None, + extract_images: bool = True, +) -> dict[str, Any]: + """ + Load a DOCX file and convert it into a SciTeX writer document. + + Parameters + ---------- + path : str | Path + Path to the .docx file. + profile : str | None + Optional profile name that specifies how to interpret Word styles + (e.g., "mdpi-ijerph", "resna-2025"). If None, "generic" is used. + extract_images : bool + If True, extract embedded images and store references. + + Returns + ------- + dict + A SciTeX writer document structure containing: + - blocks: List of document blocks (headings, paragraphs, captions, etc.) + - metadata: Profile and source file information + - images: Extracted image references (if extract_images=True) + - references: Parsed reference entries + + Examples + -------- + >>> from scitex.msword import load_docx + >>> doc = load_docx("manuscript.docx", profile="mdpi-ijerph") + >>> print(doc["metadata"]["profile"]) + 'mdpi-ijerph' + """ + path = Path(path) + profile_obj: BaseWordProfile = get_profile(profile) + reader = WordReader(profile=profile_obj, extract_images=extract_images) + return reader.read(path)
+ + + +
+[docs] +def save_docx( + writer_doc: dict[str, Any] | Any, + path: str | Path, + profile: str | None = None, + overwrite: bool = True, + template_path: str | Path | None = None, +) -> Path: + """ + Save a SciTeX writer document as a DOCX file. + + Parameters + ---------- + writer_doc : dict | Any + SciTeX writer document instance to export. + path : str | Path + Output path for the .docx file. + profile : str | None + Optional profile name that controls how sections, headings, + figures, tables and references are mapped to Word styles. + If None, "generic" is used. + overwrite : bool + If False and the file already exists, raises FileExistsError. + template_path : str | Path | None + Optional path to a Word template (.dotx/.docx) to use as base. + This allows using journal-specific formatting. + + Returns + ------- + Path + The path to the written .docx file. + + Examples + -------- + >>> from scitex.msword import save_docx + >>> save_docx(doc, "submission_resna_2025.docx", profile="resna-2025") + PosixPath('submission_resna_2025.docx') + """ + output_path = Path(path) + if output_path.exists() and not overwrite: + raise FileExistsError(f"File already exists: {output_path}") + + profile_obj: BaseWordProfile = get_profile(profile) + writer = WordWriter(profile=profile_obj, template_path=template_path) + writer.write(writer_doc, output_path) + return output_path
+ + + +
+[docs] +def convert_docx_to_tex( + input_path: str | Path, + output_path: str | Path, + profile: str | None = None, + *, + image_dir: str | Path | None = None, + link_images: bool = True, + link_mode: str = "by-number", + normalize_headings: bool = True, + validate: bool = True, +) -> Path: + """ + Convert a DOCX file directly to LaTeX. + + This is a convenience function that: + 1. Loads the DOCX file into SciTeX intermediate format + 2. (Optionally) normalizes headings + 3. (Optionally) links figure captions to images + 4. (Optionally) validates the document and adds warnings + 5. Exports to LaTeX (including figures via image_dir) + + Parameters + ---------- + input_path : str | Path + Path to the input .docx file. + output_path : str | Path + Path for the output .tex file. + profile : str | None + Word profile for interpreting styles + (e.g., "resna-2025", "iop-double-anonymous"). + image_dir : str | Path | None, optional + Directory where extracted figure image files will be saved. + If None, the LaTeX exporter will create "<tex_stem>_figures" + next to `output_path`. + link_images : bool, default True + Whether to link figure captions to extracted images so that + LaTeX can generate \\includegraphics inside figure environments. + link_mode : {"by-number", "by-proximity"}, default "by-number" + Strategy for linking captions to images: + - "by-number": Figure 1 -> first image, Figure 2 -> second image... + - "by-proximity": assign images in document order, useful when + figure numbers and image order don't match. + normalize_headings : bool, default True + If True, apply common heading normalizations + (e.g., "intro" -> "Introduction"). + validate : bool, default True + If True, run basic structural checks and populate + doc["warnings"] with any issues. + + Returns + ------- + Path + The path to the written .tex file. + + Examples + -------- + >>> from scitex.msword import convert_docx_to_tex + >>> convert_docx_to_tex( + ... "RESNA 2025 Scientific Paper Template.docx", + ... "manuscript.tex", + ... profile="resna-2025", + ... image_dir="figures", + ... ) + PosixPath('manuscript.tex') + """ + # Lazy import: convert_docx_to_tex requires scitex (or scitex-tex) for the + # final .tex serialization step. Other scitex_msword functions don't need it. + try: + from scitex_tex import export_tex + except ImportError as _e: + raise ImportError( + "convert_docx_to_tex requires the 'scitex' package (provides scitex.tex). " + "Install with: pip install scitex" + ) from _e + + # 1. DOCX -> SciTeX intermediate format + doc = load_docx(input_path, profile=profile, extract_images=True) + + # 2. Normalize headings (optional) + if normalize_headings: + doc = normalize_section_headings(doc) + + # 3. Link captions to images (optional) + if link_images and doc.get("images"): + if link_mode == "by-proximity": + doc = link_captions_to_images_by_proximity(doc) + else: + # Default: link by figure number + doc = link_captions_to_images(doc) + + # 4. Validate document structure (optional) + if validate: + doc = validate_document(doc) + + # 5. SciTeX -> LaTeX (with figures) + return export_tex(doc, output_path, image_dir=image_dir)
+ + + +__all__ = [ + "__version__", + "load_docx", + "save_docx", + "convert_docx_to_tex", + "list_profiles", + "get_profile", + "register_profile", + "BaseWordProfile", + "WordReader", + "WordWriter", + # Utility functions for post-processing + "link_captions_to_images", + "link_captions_to_images_by_proximity", + "normalize_section_headings", + "validate_document", + "create_post_import_hook", + # Diff API (BOOST v16 dogfooding) + "diff_docx", + "summarize_diff", + # Highlight API (BOOST v16 visual marking) + "mark_additions", + "mark_modifications", + "extract_highlights", + "clear_highlights", + # Bold-token preservation (BOOST v16 Japanese keyword emphasis) + "preserve_bold_tokens", + # Comment extraction (+ narrow REPLACE-grammar application) + "extract_comments", + "apply_comments_as_edits", + # Track-Changes API (BOOST post-v16 dogfooding) + "enable_track_changes", + "is_track_changes_enabled", + "wrap_as_tracked_insertion", + "wrap_as_tracked_deletion", + "extract_tracked_changes", + "accept_all_tracked_changes", + "reject_all_tracked_changes", +] +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_msword/comments.html b/src/scitex/_sphinx_html/_modules/scitex_msword/comments.html new file mode 100644 index 000000000..b2ab3405e --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_msword/comments.html @@ -0,0 +1,1031 @@ + + + + + + + + scitex_msword.comments — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_msword.comments

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Timestamp: 2026-06-02 00:00:00
+# File: src/scitex_msword/comments.py
+#
+# Part of scitex-msword (AGPL-3.0-only). See LICENSE at the repo root.
+
+"""
+Comment extraction and (limited) application for python-docx Documents.
+
+Word stores comments in ``word/comments.xml`` inside the .docx ZIP, while
+the comment *anchors* (i.e. the ranges the comment refers to) live in
+``word/document.xml`` as ``commentRangeStart`` / ``commentRangeEnd``
+sibling elements with an ``w:id`` attribute matching the comment.
+
+This module exposes :func:`extract_comments` for reading them back into
+a structured list and :func:`apply_comments_as_edits` which honors a
+narrow "REPLACE:" comment grammar to perform automated edits.
+"""
+
+from __future__ import annotations
+
+import re
+import zipfile
+from pathlib import Path
+from typing import Any, Dict, List, Optional, Union
+from xml.etree import ElementTree as ET
+
+try:
+    import docx  # type: ignore[import-untyped]
+    from docx.document import Document as DocxDocument  # type: ignore[import-untyped]
+    from docx.oxml.ns import qn  # type: ignore[import-untyped]
+
+    DOCX_AVAILABLE = True
+    _DOCX_IMPORT_ERROR: Optional[Exception] = None
+except ImportError as exc:  # pragma: no cover
+    DOCX_AVAILABLE = False
+    _DOCX_IMPORT_ERROR = exc
+    DocxDocument = None  # type: ignore[assignment,misc]
+    qn = None  # type: ignore[assignment]
+
+
+_W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
+_NS = {"w": _W_NS}
+
+# Grammar for apply_comments_as_edits — intentionally narrow.
+_REPLACE_RE = re.compile(r"^\s*REPLACE\s*:\s*(.+?)\s*$", re.IGNORECASE | re.DOTALL)
+
+
+def _ensure_docx_available() -> None:
+    if not DOCX_AVAILABLE:
+        raise ImportError(
+            "python-docx is required for scitex_msword.comments. "
+            "Install it via `pip install python-docx`."
+        ) from _DOCX_IMPORT_ERROR
+
+
+def _docx_path(doc: Union[str, Path, "DocxDocument"]) -> Optional[Path]:
+    """Return the on-disk path backing a Document, or None if in-memory."""
+    if isinstance(doc, (str, Path)):
+        return Path(doc)
+    pkg = getattr(doc, "part", None)
+    if pkg is None:
+        return None
+    pkg_pkg = getattr(pkg, "package", None)
+    if pkg_pkg is None:
+        return None
+    pkg_path = getattr(pkg_pkg, "_path", None)
+    if pkg_path:
+        return Path(pkg_path)
+    return None
+
+
+def _read_comments_xml(source: Union[str, Path, "DocxDocument"]) -> Optional[bytes]:
+    """Return the raw word/comments.xml bytes, or None if absent."""
+    if isinstance(source, (str, Path)):
+        zip_path = Path(source)
+    else:
+        # In-memory Document: read straight from the OPC part.
+        try:
+            for part in source.part.package.iter_parts():
+                if part.partname == "/word/comments.xml":
+                    return part.blob
+        except Exception:
+            pass
+        path = _docx_path(source)
+        if path is None:
+            return None
+        zip_path = path
+
+    if not zip_path.exists():
+        return None
+    try:
+        with zipfile.ZipFile(zip_path) as zf:
+            if "word/comments.xml" not in zf.namelist():
+                return None
+            return zf.read("word/comments.xml")
+    except zipfile.BadZipFile:
+        return None
+
+
+def _parse_comments_xml(blob: bytes) -> Dict[str, Dict[str, Any]]:
+    """Parse word/comments.xml into ``{comment_id: {author, text, date}}``."""
+    root = ET.fromstring(blob)
+    comments: Dict[str, Dict[str, Any]] = {}
+    for c in root.findall("w:comment", _NS):
+        cid = c.get(qn("w:id")) if qn is not None else c.get(f"{{{_W_NS}}}id")
+        if cid is None:
+            continue
+        author = (
+            c.get(qn("w:author")) if qn is not None else c.get(f"{{{_W_NS}}}author")
+        ) or ""
+        date = (
+            c.get(qn("w:date")) if qn is not None else c.get(f"{{{_W_NS}}}date")
+        ) or ""
+        texts: List[str] = []
+        for t in c.iter(f"{{{_W_NS}}}t"):
+            if t.text:
+                texts.append(t.text)
+        comments[str(cid)] = {
+            "id": int(cid) if str(cid).isdigit() else cid,
+            "author": author,
+            "date": date,
+            "text": "".join(texts),
+        }
+    return comments
+
+
+def _scan_anchors(
+    document: "DocxDocument",
+) -> Dict[str, Dict[str, Any]]:
+    """
+    Walk the document body and locate comment anchor ranges.
+
+    Returns ``{comment_id_str: {anchor_text, paragraph_range}}``.
+    """
+    paragraphs = list(document.paragraphs)
+    anchors: Dict[str, Dict[str, Any]] = {}
+    # Track which comments are currently open and at which paragraph they started.
+    open_comments: Dict[str, int] = {}
+    # Buffer for in-progress anchor text per comment id.
+    open_text: Dict[str, List[str]] = {}
+
+    id_attr = qn("w:id") if qn is not None else f"{{{_W_NS}}}id"
+
+    for pi, para in enumerate(paragraphs):
+        # We want a stream of (kind, payload) events from the paragraph XML.
+        for elem in para._p.iter():
+            tag = elem.tag
+            if tag == f"{{{_W_NS}}}commentRangeStart":
+                cid = elem.get(id_attr)
+                if cid is not None:
+                    open_comments[cid] = pi
+                    open_text.setdefault(cid, [])
+            elif tag == f"{{{_W_NS}}}commentRangeEnd":
+                cid = elem.get(id_attr)
+                if cid is not None and cid in open_comments:
+                    start_p = open_comments.pop(cid)
+                    text = "".join(open_text.pop(cid, []))
+                    anchors[cid] = {
+                        "anchor_text": text,
+                        "paragraph_range": [start_p, pi],
+                    }
+            elif tag == f"{{{_W_NS}}}t":
+                # Any text run that runs while a comment is open contributes
+                # to that comment's anchor text.
+                if elem.text and open_comments:
+                    for cid in open_comments:
+                        open_text[cid].append(elem.text)
+    return anchors
+
+
+
+[docs] +def extract_comments( + document: Union[str, Path, "DocxDocument"], +) -> List[Dict[str, Any]]: + """ + Extract Word comments from a .docx file or open Document. + + Parameters + ---------- + document : str | Path | docx.Document + Path to the .docx or an already-open Document. + + Returns + ------- + list[dict] + One entry per comment:: + + {"id": int | str, + "author": str, + "date": str, # ISO timestamp string, may be empty + "text": str, # comment body + "anchor_text": str, # text the comment is anchored to + "paragraph_range": [start, end]} + + ``anchor_text`` and ``paragraph_range`` default to ``""`` and + ``[None, None]`` when no in-document anchor can be located. + + Examples + -------- + >>> from scitex_msword.comments import extract_comments + >>> comments = extract_comments("boost-v16.docx") + >>> [c["text"] for c in comments] + ['Please rephrase this', 'REPLACE: Use the new wording'] + """ + _ensure_docx_available() + blob = _read_comments_xml(document) + if not blob: + return [] + parsed = _parse_comments_xml(blob) + + # Anchor scanning needs an open Document. + if isinstance(document, (str, Path)): + doc_obj = docx.Document(str(document)) + else: + doc_obj = document + anchors = _scan_anchors(doc_obj) + + out: List[Dict[str, Any]] = [] + for cid, meta in parsed.items(): + anchor = anchors.get(cid, {}) + out.append( + { + "id": meta["id"], + "author": meta["author"], + "date": meta["date"], + "text": meta["text"], + "anchor_text": anchor.get("anchor_text", ""), + "paragraph_range": anchor.get("paragraph_range", [None, None]), + } + ) + # Sort by numeric id when possible for stable output. + out.sort(key=lambda d: (isinstance(d["id"], str), d["id"])) + return out
+ + + +def _replace_in_paragraph(paragraph, anchor: str, replacement: str) -> bool: + """Replace ``anchor`` text in ``paragraph`` with ``replacement``. + + Returns True if a replacement occurred. + """ + text = paragraph.text + if anchor not in text: + return False + new_text = text.replace(anchor, replacement, 1) + # Rebuild paragraph runs with the new text, keeping first run's formatting. + first_run_fmt: Dict[str, Any] = {} + if paragraph.runs: + r = paragraph.runs[0] + first_run_fmt = { + "bold": r.bold, + "italic": r.italic, + "underline": r.underline, + } + # Clear existing runs. + for r in list(paragraph._p.findall(qn("w:r"))): + paragraph._p.remove(r) + new_run = paragraph.add_run(new_text) + if first_run_fmt.get("bold") is not None: + new_run.bold = first_run_fmt["bold"] + if first_run_fmt.get("italic") is not None: + new_run.italic = first_run_fmt["italic"] + if first_run_fmt.get("underline") is not None: + new_run.underline = first_run_fmt["underline"] + return True + + +
+[docs] +def apply_comments_as_edits( + document: "DocxDocument", + *, + comments: Optional[List[Dict[str, Any]]] = None, + grammar: str = "replace", +) -> Dict[str, Any]: + """ + Apply comments to the document body using a narrow grammar. + + Only the ``REPLACE:`` grammar is currently supported, i.e. a comment + whose body matches ``r"^\\s*REPLACE\\s*:\\s*(.+?)\\s*$"`` is interpreted + as "replace this comment's anchor text with the trailing payload". + Other comments are ignored. + + Parameters + ---------- + document : docx.Document + The Document to mutate in place. + comments : list[dict], optional + Pre-extracted comments (as returned by :func:`extract_comments`). + If ``None``, the comments are read from ``document`` directly. + grammar : str, default "replace" + Reserved for future expansion. Currently only ``"replace"`` + is recognised. + + Returns + ------- + dict + Summary: ``{"applied": int, "skipped": int, "details": [...]}``. + + Examples + -------- + >>> from scitex_msword.comments import apply_comments_as_edits + >>> summary = apply_comments_as_edits(doc) + >>> summary["applied"] + 2 + """ + _ensure_docx_available() + if grammar != "replace": + raise ValueError( + f"Unsupported grammar: {grammar!r}. Only 'replace' is implemented." + ) + + if comments is None: + comments = extract_comments(document) + + paragraphs = list(document.paragraphs) + applied = 0 + skipped = 0 + details: List[Dict[str, Any]] = [] + + for c in comments: + m = _REPLACE_RE.match(c.get("text", "")) + if not m: + skipped += 1 + details.append({"id": c.get("id"), "status": "skipped", "reason": "no-grammar-match"}) + continue + replacement = m.group(1) + anchor = c.get("anchor_text") or "" + if not anchor: + skipped += 1 + details.append({"id": c.get("id"), "status": "skipped", "reason": "no-anchor"}) + continue + start, end = c.get("paragraph_range", [None, None]) + if start is None or end is None: + skipped += 1 + details.append({"id": c.get("id"), "status": "skipped", "reason": "no-range"}) + continue + did_apply = False + for pi in range(max(0, start), min(end + 1, len(paragraphs))): + if _replace_in_paragraph(paragraphs[pi], anchor, replacement): + did_apply = True + break + if did_apply: + applied += 1 + details.append({"id": c.get("id"), "status": "applied"}) + else: + skipped += 1 + details.append( + {"id": c.get("id"), "status": "skipped", "reason": "anchor-not-found"} + ) + + return {"applied": applied, "skipped": skipped, "details": details}
+ + + +__all__ = ["extract_comments", "apply_comments_as_edits"] +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_msword/diff.html b/src/scitex/_sphinx_html/_modules/scitex_msword/diff.html new file mode 100644 index 000000000..481e26680 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_msword/diff.html @@ -0,0 +1,955 @@ + + + + + + + + scitex_msword.diff — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_msword.diff

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Timestamp: 2026-06-02 00:00:00
+# File: src/scitex_msword/diff.py
+#
+# Part of scitex-msword (AGPL-3.0-only). See LICENSE at the repo root.
+
+"""
+Paragraph-level diff between two DOCX documents.
+
+This module implements ``diff_docx`` which compares two .docx files (or two
+already-loaded ``python-docx`` ``Document`` objects) and returns a list of
+paragraph-level operations describing the changes.
+
+The diff is computed with ``difflib.SequenceMatcher`` over paragraph text,
+and per-paragraph run-level formatting deltas (bold / italic / underline /
+font / highlight) are also captured for ``modify`` operations.
+
+Typical usage
+-------------
+    >>> from scitex_msword.diff import diff_docx
+    >>> ops = diff_docx("v15.docx", "v16.docx")
+    >>> for op in ops:
+    ...     print(op["op"], op["index"], op.get("text_b") or op.get("text_a"))
+"""
+
+from __future__ import annotations
+
+import difflib
+from pathlib import Path
+from typing import Any, Dict, List, Optional, Tuple, Union
+
+try:
+    import docx  # type: ignore[import-untyped]
+    from docx.document import Document as DocxDocument  # type: ignore[import-untyped]
+
+    DOCX_AVAILABLE = True
+    _DOCX_IMPORT_ERROR: Optional[Exception] = None
+except ImportError as exc:  # pragma: no cover — install-time issue
+    DOCX_AVAILABLE = False
+    _DOCX_IMPORT_ERROR = exc
+    DocxDocument = None  # type: ignore[assignment,misc]
+
+
+DocLike = Union[str, Path, "DocxDocument"]
+
+# Run-level attributes we track for "modify" diffs.
+_RUN_ATTRS = ("bold", "italic", "underline", "font_name", "font_size", "highlight")
+
+
+def _ensure_docx_available() -> None:
+    if not DOCX_AVAILABLE:
+        raise ImportError(
+            "python-docx is required for scitex_msword.diff. "
+            "Install it via `pip install python-docx`."
+        ) from _DOCX_IMPORT_ERROR
+
+
+def _load(doc_like: DocLike) -> "DocxDocument":
+    """Coerce a path or an already-open Document into a Document."""
+    _ensure_docx_available()
+    if isinstance(doc_like, (str, Path)):
+        return docx.Document(str(doc_like))
+    return doc_like
+
+
+def _extract_run_info(run) -> Dict[str, Any]:
+    """Extract diff-relevant attributes from a python-docx Run."""
+    info: Dict[str, Any] = {
+        "text": run.text,
+        "bold": bool(run.bold) if run.bold is not None else False,
+        "italic": bool(run.italic) if run.italic is not None else False,
+        "underline": run.underline is not None and bool(run.underline),
+    }
+    try:
+        if run.font.name:
+            info["font_name"] = run.font.name
+    except Exception:
+        pass
+    try:
+        if run.font.size:
+            info["font_size"] = run.font.size.pt
+    except Exception:
+        pass
+    try:
+        hl = run.font.highlight_color
+        if hl is not None:
+            # WD_COLOR_INDEX enum -> human-readable lowercase name.
+            # str(hl) is e.g. "TURQUOISE (3)"; .name is "TURQUOISE".
+            name = getattr(hl, "name", str(hl)).split(".")[-1].split(" ")[0]
+            info["highlight"] = name.lower()
+    except Exception:
+        pass
+    return info
+
+
+def _paragraph_texts_and_runs(
+    document: "DocxDocument",
+) -> Tuple[List[str], List[List[Dict[str, Any]]]]:
+    """Return per-paragraph (text, run-info-list) for the document body."""
+    texts: List[str] = []
+    runs: List[List[Dict[str, Any]]] = []
+    for para in document.paragraphs:
+        texts.append(para.text)
+        runs.append([_extract_run_info(r) for r in para.runs])
+    return texts, runs
+
+
+def _diff_runs(
+    runs_a: List[Dict[str, Any]], runs_b: List[Dict[str, Any]]
+) -> List[Dict[str, Any]]:
+    """
+    Compare run lists by index and return a list of run-level changes.
+
+    Only the attributes in ``_RUN_ATTRS`` are tracked. For run-count
+    mismatches, the extra runs are reported as added/removed.
+    """
+    changes: List[Dict[str, Any]] = []
+    common = min(len(runs_a), len(runs_b))
+    for i in range(common):
+        ra, rb = runs_a[i], runs_b[i]
+        delta: Dict[str, Any] = {}
+        for attr in _RUN_ATTRS:
+            va, vb = ra.get(attr), rb.get(attr)
+            if va != vb:
+                delta[attr] = {"a": va, "b": vb}
+        if ra.get("text") != rb.get("text"):
+            delta["text"] = {"a": ra.get("text"), "b": rb.get("text")}
+        if delta:
+            changes.append({"run_index": i, "delta": delta})
+    if len(runs_a) > common:
+        for i in range(common, len(runs_a)):
+            changes.append({"run_index": i, "removed": runs_a[i]})
+    if len(runs_b) > common:
+        for i in range(common, len(runs_b)):
+            changes.append({"run_index": i, "added": runs_b[i]})
+    return changes
+
+
+
+[docs] +def diff_docx( + a: DocLike, + b: DocLike, + *, + include_run_diff: bool = True, +) -> List[Dict[str, Any]]: + """ + Compute paragraph-level diff between two DOCX documents. + + Parameters + ---------- + a, b : str | Path | docx.Document + Inputs to compare. May be paths or already-loaded Documents. + include_run_diff : bool, default True + If True, ``modify`` operations include a ``runs_changed`` field + listing the run-level formatting deltas. + + Returns + ------- + list[dict] + Each entry is one of:: + + {"op": "equal", "index": int, "text_a": str, "text_b": str} + {"op": "insert", "index": int, "text_a": None, "text_b": str} + {"op": "delete", "index": int, "text_a": str, "text_b": None} + {"op": "modify", "index": int, "text_a": str, "text_b": str, + "runs_changed": [...]} + + ``index`` refers to the paragraph index in document ``b`` for + ``equal``/``insert``/``modify`` operations, and to the paragraph + index in document ``a`` for ``delete`` operations. + + Examples + -------- + >>> from scitex_msword.diff import diff_docx + >>> ops = diff_docx("v15.docx", "v16.docx") + >>> changes = [o for o in ops if o["op"] != "equal"] + """ + doc_a = _load(a) + doc_b = _load(b) + + texts_a, runs_a = _paragraph_texts_and_runs(doc_a) + texts_b, runs_b = _paragraph_texts_and_runs(doc_b) + + matcher = difflib.SequenceMatcher(a=texts_a, b=texts_b, autojunk=False) + ops: List[Dict[str, Any]] = [] + + for tag, i1, i2, j1, j2 in matcher.get_opcodes(): + if tag == "equal": + for k in range(i2 - i1): + ops.append( + { + "op": "equal", + "index": j1 + k, + "text_a": texts_a[i1 + k], + "text_b": texts_b[j1 + k], + } + ) + elif tag == "delete": + for k in range(i2 - i1): + ops.append( + { + "op": "delete", + "index": i1 + k, + "text_a": texts_a[i1 + k], + "text_b": None, + } + ) + elif tag == "insert": + for k in range(j2 - j1): + ops.append( + { + "op": "insert", + "index": j1 + k, + "text_a": None, + "text_b": texts_b[j1 + k], + } + ) + elif tag == "replace": + # Pair up the replaced ranges; surplus on either side becomes + # delete / insert. + paired = min(i2 - i1, j2 - j1) + for k in range(paired): + ai, bi = i1 + k, j1 + k + entry: Dict[str, Any] = { + "op": "modify", + "index": bi, + "text_a": texts_a[ai], + "text_b": texts_b[bi], + } + if include_run_diff: + entry["runs_changed"] = _diff_runs(runs_a[ai], runs_b[bi]) + ops.append(entry) + for k in range(paired, i2 - i1): + ai = i1 + k + ops.append( + { + "op": "delete", + "index": ai, + "text_a": texts_a[ai], + "text_b": None, + } + ) + for k in range(paired, j2 - j1): + bi = j1 + k + ops.append( + { + "op": "insert", + "index": bi, + "text_a": None, + "text_b": texts_b[bi], + } + ) + + return ops
+ + + +
+[docs] +def summarize_diff(ops: List[Dict[str, Any]]) -> Dict[str, int]: + """ + Convenience helper: count operations by type. + + Parameters + ---------- + ops : list[dict] + Output of :func:`diff_docx`. + + Returns + ------- + dict[str, int] + Mapping ``{"equal": n, "insert": n, "delete": n, "modify": n}``. + """ + summary = {"equal": 0, "insert": 0, "delete": 0, "modify": 0} + for op in ops: + summary[op["op"]] = summary.get(op["op"], 0) + 1 + return summary
+ + + +__all__ = ["diff_docx", "summarize_diff"] +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_msword/highlights.html b/src/scitex/_sphinx_html/_modules/scitex_msword/highlights.html new file mode 100644 index 000000000..f99c087e1 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_msword/highlights.html @@ -0,0 +1,973 @@ + + + + + + + + scitex_msword.highlights — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_msword.highlights

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Timestamp: 2026-06-02 00:00:00
+# File: src/scitex_msword/highlights.py
+#
+# Part of scitex-msword (AGPL-3.0-only). See LICENSE at the repo root.
+
+"""
+Visual-mark / highlight utilities for python-docx Documents.
+
+This module is used by the BOOST v16 dogfooding workflow to visualize:
+
+- *additions* by the operator (turquoise highlight, by convention)
+- *modifications* to operator-supplied content (magenta highlight)
+
+plus a generic :func:`extract_highlights` to inspect what colors are
+present in a document and which runs carry them.
+
+Color names mirror Word's ``WD_COLOR_INDEX`` enum (case-insensitive):
+``turquoise``, ``pink``, ``yellow``, ``green``, ``blue``, ``red``,
+``gray_25``, ``gray_50``, ``black``, ``white``, ``dark_red``,
+``dark_yellow``, ``dark_blue``, ``teal``, ``violet``, and ``auto``.
+
+For convenience the following BOOST-v16 aliases are also accepted:
+
+- ``magenta`` -> ``pink`` (Word's bright-pink highlight)
+- ``purple``  -> ``violet``
+- ``cyan``    -> ``turquoise``
+"""
+
+from __future__ import annotations
+
+from collections import defaultdict
+from typing import Any, Dict, Iterable, List, Optional, Tuple
+
+try:
+    from docx.document import Document as DocxDocument  # type: ignore[import-untyped]
+    from docx.enum.text import WD_COLOR_INDEX  # type: ignore[import-untyped]
+
+    DOCX_AVAILABLE = True
+    _DOCX_IMPORT_ERROR: Optional[Exception] = None
+except ImportError as exc:  # pragma: no cover
+    DOCX_AVAILABLE = False
+    _DOCX_IMPORT_ERROR = exc
+    WD_COLOR_INDEX = None  # type: ignore[assignment]
+    DocxDocument = None  # type: ignore[assignment,misc]
+
+
+# Canonical addition / modification colors (mirrors BOOST v16 convention).
+ADDITION_COLOR = "turquoise"
+MODIFICATION_COLOR = "magenta"
+
+# Map lowercase / underscore-tolerant color name -> WD_COLOR_INDEX value.
+# Lazily populated on first use so the module is import-safe without docx.
+_COLOR_NAME_MAP: Dict[str, Any] = {}
+
+
+def _ensure_docx_available() -> None:
+    if not DOCX_AVAILABLE:
+        raise ImportError(
+            "python-docx is required for scitex_msword.highlights. "
+            "Install it via `pip install python-docx`."
+        ) from _DOCX_IMPORT_ERROR
+
+
+def _color_map() -> Dict[str, Any]:
+    """Lazy-build the lowercase -> WD_COLOR_INDEX mapping."""
+    if _COLOR_NAME_MAP:
+        return _COLOR_NAME_MAP
+    _ensure_docx_available()
+    for member in WD_COLOR_INDEX:  # type: ignore[union-attr]
+        name = member.name.lower()
+        _COLOR_NAME_MAP[name] = member
+        # Allow callers to use either "gray25" or "gray_25" forms.
+        if "_" in name:
+            _COLOR_NAME_MAP[name.replace("_", "")] = member
+    # Friendly aliases used by the BOOST v16 workflow.
+    _COLOR_NAME_MAP["magenta"] = WD_COLOR_INDEX.PINK  # type: ignore[union-attr]
+    _COLOR_NAME_MAP["purple"] = WD_COLOR_INDEX.VIOLET  # type: ignore[union-attr]
+    _COLOR_NAME_MAP["cyan"] = WD_COLOR_INDEX.TURQUOISE  # type: ignore[union-attr]
+    return _COLOR_NAME_MAP
+
+
+def _resolve_color(color):
+    """Translate a string color name (or pass-through enum) into WD_COLOR_INDEX."""
+    if color is None:
+        _ensure_docx_available()
+        return WD_COLOR_INDEX.AUTO  # type: ignore[union-attr]
+    if isinstance(color, str):
+        key = color.strip().lower().replace("-", "_")
+        cmap = _color_map()
+        if key not in cmap:
+            raise ValueError(
+                f"Unknown highlight color: {color!r}. "
+                f"Known: {sorted(set(cmap))[:12]}..."
+            )
+        return cmap[key]
+    return color
+
+
+def _iter_target_runs(document: "DocxDocument", targets: Iterable[Tuple[int, int]]):
+    """Yield (run_obj, (paragraph_idx, run_idx)) pairs for valid targets."""
+    paragraphs = list(document.paragraphs)
+    for pi, ri in targets:
+        if 0 <= pi < len(paragraphs):
+            runs = paragraphs[pi].runs
+            if 0 <= ri < len(runs):
+                yield runs[ri], (pi, ri)
+
+
+def _apply_highlight(
+    document: "DocxDocument",
+    runs: Iterable[Tuple[int, int]],
+    color,
+) -> "DocxDocument":
+    """Apply ``color`` highlight to every (paragraph_idx, run_idx) target."""
+    _ensure_docx_available()
+    wd_color = _resolve_color(color)
+    for run, _ in _iter_target_runs(document, runs):
+        run.font.highlight_color = wd_color
+    return document
+
+
+
+[docs] +def mark_additions( + document: "DocxDocument", + runs: Iterable[Tuple[int, int]], + color: str = ADDITION_COLOR, +) -> "DocxDocument": + """ + Highlight the runs that the operator (or agent) *added* to the document. + + Parameters + ---------- + document : docx.Document + The Document to mutate in place. + runs : iterable of (paragraph_idx, run_idx) + Targets to highlight. Out-of-range indices are skipped silently. + color : str, default "turquoise" + Color name. See module docstring for the supported palette. + + Returns + ------- + docx.Document + The same Document object, mutated. + + Examples + -------- + >>> from scitex_msword.highlights import mark_additions + >>> doc = mark_additions(doc, [(3, 0), (5, 2)]) # default turquoise + """ + return _apply_highlight(document, runs, color)
+ + + +
+[docs] +def mark_modifications( + document: "DocxDocument", + runs: Iterable[Tuple[int, int]], + color: str = MODIFICATION_COLOR, +) -> "DocxDocument": + """ + Highlight the runs that the operator (or agent) *modified* in the document. + + Parameters + ---------- + document : docx.Document + The Document to mutate in place. + runs : iterable of (paragraph_idx, run_idx) + Targets to highlight. Out-of-range indices are skipped silently. + color : str, default "magenta" + Color name. See module docstring for the supported palette. + + Returns + ------- + docx.Document + The same Document object, mutated. + + Examples + -------- + >>> from scitex_msword.highlights import mark_modifications + >>> doc = mark_modifications(doc, [(7, 1)]) # default magenta + """ + return _apply_highlight(document, runs, color)
+ + + +def _run_highlight_name(run) -> Optional[str]: + """Return the lowercase highlight color name for the run, or None.""" + try: + hl = run.font.highlight_color + except Exception: + return None + if hl is None: + return None + # WD_COLOR_INDEX members expose .name; str() includes the int code, + # which is unhelpful for bucketing. + name = getattr(hl, "name", str(hl)).split(".")[-1].lower() + # Strip any trailing " (N)" leftover defensively. + name = name.split(" ")[0] + if name in ("auto", "inherited"): + return None + return name + + +
+[docs] +def extract_highlights( + document: "DocxDocument", + by_color: bool = True, +) -> Dict[str, List[Dict[str, Any]]]: + """ + Extract highlighted runs from a document, grouped by color. + + Parameters + ---------- + document : docx.Document + The document to scan. + by_color : bool, default True + If True (default), return ``{color_name: [run_info, ...]}``. + If False, return a single ``{"all": [run_info, ...]}`` bucket + with each entry's ``color`` field populated. + + Returns + ------- + dict[str, list[dict]] + Mapping from color name to a list of run info dicts of shape:: + + {"paragraph": int, "run": int, "text": str, "color": str} + + Examples + -------- + >>> from scitex_msword.highlights import extract_highlights + >>> by_color = extract_highlights(doc) + >>> by_color.get("turquoise", []) + [{'paragraph': 3, 'run': 0, 'text': '...', 'color': 'turquoise'}] + """ + _ensure_docx_available() + buckets: Dict[str, List[Dict[str, Any]]] = defaultdict(list) + for pi, para in enumerate(document.paragraphs): + for ri, run in enumerate(para.runs): + name = _run_highlight_name(run) + if not name: + continue + entry = { + "paragraph": pi, + "run": ri, + "text": run.text, + "color": name, + } + bucket = name if by_color else "all" + buckets[bucket].append(entry) + return dict(buckets)
+ + + +
+[docs] +def clear_highlights( + document: "DocxDocument", + colors: Optional[Iterable[str]] = None, +) -> "DocxDocument": + """ + Remove highlights from all runs (optionally only for the listed colors). + + Parameters + ---------- + document : docx.Document + Document to mutate in place. + colors : iterable of str, optional + If provided, only runs with one of these highlight colors are + cleared. If ``None`` (default), every highlighted run is cleared. + + Returns + ------- + docx.Document + The same Document object, mutated. + """ + _ensure_docx_available() + target = None + if colors is not None: + target = {c.strip().lower().replace("-", "_") for c in colors} + for para in document.paragraphs: + for run in para.runs: + name = _run_highlight_name(run) + if name and (target is None or name in target): + run.font.highlight_color = WD_COLOR_INDEX.AUTO # type: ignore[union-attr] + return document
+ + + +__all__ = [ + "ADDITION_COLOR", + "MODIFICATION_COLOR", + "mark_additions", + "mark_modifications", + "extract_highlights", + "clear_highlights", +] +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_msword/profiles.html b/src/scitex/_sphinx_html/_modules/scitex_msword/profiles.html new file mode 100644 index 000000000..c43d57cf5 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_msword/profiles.html @@ -0,0 +1,1086 @@ + + + + + + + + scitex_msword.profiles — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_msword.profiles

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Timestamp: 2025-12-11 15:15:00
+# File: /home/ywatanabe/proj/scitex-code/src/scitex/msword/profiles.py
+
+"""
+Profiles for mapping MS Word styles to SciTeX writer structures.
+
+Each profile corresponds to a journal / conference template, such as:
+- "generic"
+- "mdpi-ijerph"
+- "resna-2025"
+- "iop-double-anonymous"
+
+The profiles define:
+- Which Word style names correspond to section headings
+- How to detect captions for figures and tables
+- How to handle references, lists, equations, etc.
+- Layout settings (columns, margins, fonts)
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass, field
+from typing import Callable, Dict, List, Optional
+
+
+
+[docs] +@dataclass +class BaseWordProfile: + """ + Base configuration for mapping between DOCX and SciTeX writer documents. + + Attributes + ---------- + name : str + Profile identifier (e.g., "mdpi-ijerph"). + description : str + Human-readable description. + heading_styles : dict[int, str] + Mapping from section depth (1, 2, 3...) to Word style names + (e.g., {1: "Heading 1", 2: "Heading 2"}). + caption_style : str + Word style name used for figure/table captions. + normal_style : str + Default paragraph style. + reference_section_titles : list[str] + Titles that indicate the start of the reference section. + figure_caption_prefixes : list[str] + Prefixes that identify figure captions (e.g., ["Figure", "Fig."]). + table_caption_prefixes : list[str] + Prefixes that identify table captions (e.g., ["Table"]). + list_styles : dict[str, str] + Mapping for list styles (bullet, numbered). + equation_style : str | None + Style name for equations, if any. + columns : int + Number of columns in the layout (1 or 2). + double_anonymous : bool + Whether this profile requires double-anonymous formatting. + """ + + name: str + description: str + heading_styles: Dict[int, str] = field(default_factory=dict) + caption_style: str = "Caption" + normal_style: str = "Normal" + reference_section_titles: List[str] = field( + default_factory=lambda: ["References", "REFERENCES"] + ) + figure_caption_prefixes: List[str] = field( + default_factory=lambda: ["Figure", "Fig.", "Fig"] + ) + table_caption_prefixes: List[str] = field( + default_factory=lambda: ["Table", "Tab.", "Tab"] + ) + list_styles: Dict[str, str] = field( + default_factory=lambda: { + "bullet": "List Bullet", + "numbered": "List Number", + } + ) + equation_style: Optional[str] = None + columns: int = 1 + double_anonymous: bool = False + + # Optional layout / typography hints (used by profiles like boost-2026). + # These are advisory: writer code may consult them to customize fonts, + # heading shading, and line spacing without hard-coding per-profile logic. + body_font: Optional[str] = None + body_font_size_pt: Optional[float] = None + heading_background_hex: Optional[str] = None + line_spacing: Optional[float] = None + + # Post-processing hooks + post_import_hooks: List[Callable] = field(default_factory=list) + pre_export_hooks: List[Callable] = field(default_factory=list)
+ + + +# --- Concrete profiles ------------------------------------------------------ + + +def _generic_profile() -> BaseWordProfile: + """ + Generic Word template profile. + + This profile is intentionally conservative and assumes that: + - "Heading 1/2/3" are used for section headings. + - "Caption" is used for figure/table captions. + - "Normal" is the default body text. + + This should work reasonably well for many simple manuscripts. + """ + return BaseWordProfile( + name="generic", + description="Generic Word mapping with standard Heading styles.", + heading_styles={ + 1: "Heading 1", + 2: "Heading 2", + 3: "Heading 3", + 4: "Heading 4", + }, + caption_style="Caption", + normal_style="Normal", + reference_section_titles=["References", "REFERENCES", "Bibliography"], + ) + + +def _mdpi_ijerph_profile() -> BaseWordProfile: + """ + MDPI IJERPH template profile. + + Based on the MDPI Word template structure: + - Section headings use built-in heading styles. + - References section is titled "References". + - Single column layout. + - Specific section order: Introduction, Materials and Methods, + Results, Discussion, Conclusions. + """ + return BaseWordProfile( + name="mdpi-ijerph", + description="MDPI IJERPH (Int. J. Environ. Res. Public Health) Word template.", + heading_styles={ + 1: "Heading 1", + 2: "Heading 2", + 3: "Heading 3", + }, + caption_style="Caption", + normal_style="Normal", + reference_section_titles=["References"], + columns=1, + ) + + +def _resna_2025_profile() -> BaseWordProfile: + """ + RESNA 2025 scientific paper template profile. + + The RESNA template: + - Uses all-caps section headings (INTRODUCTION, METHODS, etc.) + - Strict 4-page layout + - Two-column format + """ + return BaseWordProfile( + name="resna-2025", + description="RESNA 2025 Scientific Paper Word template.", + heading_styles={ + 1: "Heading 1", # INTRODUCTION, METHODS, etc. + 2: "Heading 2", # First-level sub-heading + }, + caption_style="Caption", + normal_style="Normal", + reference_section_titles=["References", "REFERENCES"], + columns=2, + ) + + +def _iop_double_anonymous_profile() -> BaseWordProfile: + """ + IOP double-anonymous Word template profile. + + The IOP template uses custom styles: + - IOPH1, IOPH2, IOPH3 for headings + - IOPTitle for title + - IOPAbsText for abstract + - IOPAff for affiliations + - Requires removal of author-identifying information + """ + return BaseWordProfile( + name="iop-double-anonymous", + description="IOP double-anonymous Word template.", + heading_styles={ + 1: "IOPH1", + 2: "IOPH2", + 3: "IOPH3", + }, + caption_style="Caption", + normal_style="Normal", + reference_section_titles=["References"], + double_anonymous=True, + ) + + +def _ieee_profile() -> BaseWordProfile: + """ + IEEE conference/journal template profile. + + The IEEE template: + - Two-column format + - Roman numeral section numbering + - Specific citation style + """ + return BaseWordProfile( + name="ieee", + description="IEEE conference/journal Word template.", + heading_styles={ + 1: "Heading 1", + 2: "Heading 2", + 3: "Heading 3", + }, + caption_style="Caption", + normal_style="Normal", + reference_section_titles=["References", "REFERENCES"], + columns=2, + ) + + +def _springer_profile() -> BaseWordProfile: + """ + Springer Nature journal template profile. + """ + return BaseWordProfile( + name="springer", + description="Springer Nature journal Word template.", + heading_styles={ + 1: "Heading 1", + 2: "Heading 2", + 3: "Heading 3", + }, + caption_style="Caption", + normal_style="Normal", + reference_section_titles=["References"], + columns=1, + ) + + +def _elsevier_profile() -> BaseWordProfile: + """ + Elsevier journal template profile. + """ + return BaseWordProfile( + name="elsevier", + description="Elsevier journal Word template.", + heading_styles={ + 1: "Heading 1", + 2: "Heading 2", + 3: "Heading 3", + }, + caption_style="Caption", + normal_style="Normal", + reference_section_titles=["References"], + columns=1, + ) + + +def _boost_2026_profile() -> BaseWordProfile: + """ + JST BOOST 2026 grant application template profile. + + Layout convention (per BOOST v16 dogfooding): + + - Body text: 10.5pt MS Gothic + - Headings: bold, with light-grey (#D9D9D9) background shading + - Line spacing: 1.0 (single) + - Single column + + The advisory ``body_font`` / ``body_font_size_pt`` / + ``heading_background_hex`` / ``line_spacing`` fields let the writer + layer (or downstream tooling such as the BOOST v16 builder) pick + these up without hard-coding per-document logic. + """ + return BaseWordProfile( + name="boost-2026", + description="JST BOOST 2026 grant application Word template.", + heading_styles={ + 1: "Heading 1", + 2: "Heading 2", + 3: "Heading 3", + }, + caption_style="Caption", + normal_style="Normal", + reference_section_titles=["参考文献", "References"], + columns=1, + body_font="MS Gothic", + body_font_size_pt=10.5, + heading_background_hex="D9D9D9", + line_spacing=1.0, + ) + + +# Registry of known profiles +_PROFILES: Dict[str, BaseWordProfile] = { + "generic": _generic_profile(), + "mdpi-ijerph": _mdpi_ijerph_profile(), + "mdpi": _mdpi_ijerph_profile(), # Alias + "resna-2025": _resna_2025_profile(), + "resna": _resna_2025_profile(), # Alias + "iop-double-anonymous": _iop_double_anonymous_profile(), + "iop": _iop_double_anonymous_profile(), # Alias + "ieee": _ieee_profile(), + "springer": _springer_profile(), + "elsevier": _elsevier_profile(), + "boost-2026": _boost_2026_profile(), + "boost": _boost_2026_profile(), # Alias +} + + +
+[docs] +def list_profiles() -> list[str]: + """ + List available MS Word profiles. + + Returns + ------- + list[str] + List of profile names (e.g., ["generic", "mdpi-ijerph", ...]). + + Examples + -------- + >>> from scitex.msword import list_profiles + >>> profiles = list_profiles() + >>> "generic" in profiles + True + """ + return sorted(_PROFILES.keys())
+ + + +
+[docs] +def get_profile(name: str | None) -> BaseWordProfile: + """ + Get a Word profile by name. + + Parameters + ---------- + name : str | None + Profile name. If None, "generic" is used. + + Returns + ------- + BaseWordProfile + The requested profile. + + Raises + ------ + KeyError + If the profile name is unknown. + + Examples + -------- + >>> from scitex.msword import get_profile + >>> profile = get_profile("mdpi-ijerph") + >>> profile.columns + 1 + """ + if name is None: + return _PROFILES["generic"] + try: + return _PROFILES[name] + except KeyError as exc: + available = ", ".join(list_profiles()) + raise KeyError( + f"Unknown MS Word profile: {name!r}. " f"Available profiles: {available}" + ) from exc
+ + + +
+[docs] +def register_profile(profile: BaseWordProfile) -> None: + """ + Register a custom Word profile. + + Parameters + ---------- + profile : BaseWordProfile + The profile to register. + + Examples + -------- + >>> from scitex.msword import BaseWordProfile, register_profile + >>> custom = BaseWordProfile( + ... name="my-journal", + ... description="My custom journal template", + ... heading_styles={1: "Title", 2: "Subtitle"}, + ... ) + >>> register_profile(custom) + >>> "my-journal" in list_profiles() + True + """ + _PROFILES[profile.name] = profile
+ + + +__all__ = [ + "BaseWordProfile", + "list_profiles", + "get_profile", + "register_profile", +] +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_msword/reader.html b/src/scitex/_sphinx_html/_modules/scitex_msword/reader.html new file mode 100644 index 000000000..d9ecc4ecd --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_msword/reader.html @@ -0,0 +1,1461 @@ + + + + + + + + scitex_msword.reader — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_msword.reader

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Timestamp: 2025-12-11 15:15:00
+# File: /home/ywatanabe/proj/scitex-code/src/scitex/msword/reader.py
+
+"""
+DOCX -> SciTeX writer document converter.
+
+This module reads MS Word .docx files and converts them into
+SciTeX's intermediate document format for further processing.
+"""
+
+from __future__ import annotations
+
+import hashlib
+import re
+from datetime import datetime
+from pathlib import Path
+from typing import Any, Dict, List, Optional, Tuple
+
+from .profiles import BaseWordProfile
+
+# Lazy import for python-docx
+try:
+    import docx
+    from docx.document import Document as DocxDocument
+    from docx.oxml.ns import qn
+    from docx.shared import Inches, Pt
+
+    DOCX_AVAILABLE = True
+    _DOCX_IMPORT_ERROR = None
+except ImportError as exc:
+    DOCX_AVAILABLE = False
+    _DOCX_IMPORT_ERROR = exc
+    DocxDocument = None
+
+# Common academic section headings for heuristic detection
+COMMON_SECTION_HEADINGS = {
+    "abstract",
+    "introduction",
+    "background",
+    "literature review",
+    "methods",
+    "methodology",
+    "materials and methods",
+    "experimental",
+    "results",
+    "findings",
+    "analysis",
+    "discussion",
+    "conclusions",
+    "conclusion",
+    "summary",
+    "acknowledgements",
+    "acknowledgments",
+    "acknowledgement",
+    "references",
+    "bibliography",
+    "works cited",
+    "appendix",
+    "appendices",
+    "supplementary",
+    "supplementary material",
+}
+
+# Caption patterns for robust detection
+CAPTION_PATTERNS = [
+    # Figure patterns
+    (r"^(figure|fig\.?)\s*(\d+)[\.:\s]*(.*)$", "figure"),
+    (r"^(scheme)\s*(\d+)[\.:\s]*(.*)$", "scheme"),
+    (r"^(chart)\s*(\d+)[\.:\s]*(.*)$", "chart"),
+    (r"^(graph)\s*(\d+)[\.:\s]*(.*)$", "graph"),
+    (r"^(plate)\s*(\d+)[\.:\s]*(.*)$", "plate"),
+    (r"^(illustration)\s*(\d+)[\.:\s]*(.*)$", "illustration"),
+    # Table patterns
+    (r"^(table|tbl\.?)\s*(\d+)[\.:\s]*(.*)$", "table"),
+    # Equation patterns
+    (r"^(equation|eq\.?)\s*(\d+)[\.:\s]*(.*)$", "equation"),
+    # Listing/code patterns
+    (r"^(listing|code)\s*(\d+)[\.:\s]*(.*)$", "listing"),
+    # Algorithm patterns
+    (r"^(algorithm|alg\.?)\s*(\d+)[\.:\s]*(.*)$", "algorithm"),
+]
+
+
+
+[docs] +class WordReader: + """ + Read a DOCX file and convert it into a SciTeX writer document. + + This reader focuses on: + - Sections (via heading styles) + - Plain paragraphs + - Figure/table captions (via caption style) + - Embedded images extraction + - References section boundary detection + - Basic formatting (bold, italic) + + The output is a structured intermediate representation that can be + easily fed into `scitex.writer` or exported to LaTeX/other formats. + """ + +
+[docs] + def __init__( + self, + profile: BaseWordProfile, + extract_images: bool = True, + ): + """ + Parameters + ---------- + profile : BaseWordProfile + Mapping between Word styles and SciTeX writer semantics. + extract_images : bool + Whether to extract embedded images from the document. + """ + if not DOCX_AVAILABLE: + raise ImportError( + "python-docx is required for scitex.msword.WordReader. " + "Install it via `pip install python-docx`." + ) from _DOCX_IMPORT_ERROR + self.profile = profile + self.extract_images = extract_images
+ + +
+[docs] + def read(self, path: Path) -> Dict[str, Any]: + """ + Read a DOCX file and return a SciTeX writer document. + + Parameters + ---------- + path : Path + Path to the DOCX file. + + Returns + ------- + dict + SciTeX writer document structure with keys: + - blocks: List of document blocks + - metadata: Profile and source information + - images: Extracted image data (if extract_images=True) + - references: Parsed reference entries + - warnings: List of conversion warnings + """ + doc = docx.Document(str(path)) + + # Initialize result structure + result: Dict[str, Any] = { + "blocks": [], + "metadata": { + "profile": self.profile.name, + "source_file": str(path), + "import_timestamp": datetime.now().isoformat(), + }, + "images": [], + "references": [], + "warnings": [], + } + + # Extract document properties if available + result["metadata"].update(self._extract_metadata(doc)) + + # Process paragraphs and tables + blocks = self._process_body(doc, result) + result["blocks"] = blocks + + # Extract images + if self.extract_images: + result["images"] = self._extract_images(doc, path) + + # Parse references section + result["references"] = self._parse_references(blocks) + + # Run post-import hooks + for hook in self.profile.post_import_hooks: + result = hook(result) + + return result
+ + + def _extract_metadata(self, doc: DocxDocument) -> Dict[str, Any]: + """Extract document metadata (title, author, etc.).""" + metadata = {} + try: + core_props = doc.core_properties + if core_props.title: + metadata["title"] = core_props.title + if core_props.author: + metadata["author"] = core_props.author + if core_props.subject: + metadata["subject"] = core_props.subject + if core_props.keywords: + metadata["keywords"] = core_props.keywords + if core_props.created: + metadata["created"] = core_props.created.isoformat() + if core_props.modified: + metadata["modified"] = core_props.modified.isoformat() + except Exception: + pass # Metadata extraction is optional + return metadata + + def _process_body( + self, + doc: DocxDocument, + result: Dict[str, Any], + ) -> List[Dict[str, Any]]: + """Process document body: paragraphs and tables.""" + blocks: List[Dict[str, Any]] = [] + in_reference_section = False + block_index = 0 + + # Build rel_id -> hash map for image detection + rel_to_hash = {} + if self.extract_images: + for rel_id, rel in doc.part.rels.items(): + if "image" in rel.reltype: + image_bytes = rel.target_part.blob + image_hash = hashlib.md5(image_bytes).hexdigest()[:12] + rel_to_hash[rel_id] = image_hash + + # Namespace for picture detection + pic_ns = {"pic": "http://schemas.openxmlformats.org/drawingml/2006/picture"} + a_ns = {"a": "http://schemas.openxmlformats.org/drawingml/2006/main"} + r_ns = { + "r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships" + } + + for element in doc.element.body: + tag = element.tag.split("}")[-1] # Remove namespace + + if tag == "p": + # Process paragraph + para = docx.text.paragraph.Paragraph(element, doc) + + # Detect inline images in this paragraph + if self.extract_images: + for run in para.runs: + # Check for drawing elements containing pictures + drawings = run.element.findall(".//a:blip", namespaces=a_ns) + for blip in drawings: + embed_attr = qn("r:embed") + rel_id = blip.get(embed_attr) + if rel_id and rel_id in rel_to_hash: + blocks.append( + { + "index": block_index, + "type": "image", + "image_hash": rel_to_hash[rel_id], + "rel_id": rel_id, + } + ) + block_index += 1 + + block = self._process_paragraph(para, in_reference_section, block_index) + if block: + # Check if entering references section + if block["type"] == "heading" and block["text"] in ( + self.profile.reference_section_titles + ): + in_reference_section = True + block["is_reference_header"] = True + + blocks.append(block) + block_index += 1 + + elif tag == "tbl": + # Process table + table = docx.table.Table(element, doc) + block = self._process_table(table, block_index) + blocks.append(block) + block_index += 1 + + return blocks + + def _process_paragraph( + self, + para, + in_reference_section: bool, + block_index: int, + ) -> Optional[Dict[str, Any]]: + """Process a single paragraph.""" + style_name = (para.style.name or "").strip() if para.style else "" + text = para.text.strip() + + if not text: + return None + + # Extract runs with formatting info + runs = self._extract_runs(para) + + # Base block structure + block: Dict[str, Any] = { + "index": block_index, + "text": text, + "style": style_name, + "runs": runs, + } + + # Check for equations (OMML) + equation_latex = self._extract_equation(para) + if equation_latex: + block["type"] = "equation" + block["latex"] = equation_latex + return block + + # Detect heading (style-based first, then heuristic) + level = self._detect_heading(para, style_name, text, runs) + if level is not None: + block["type"] = "heading" + block["level"] = level + block["detection_method"] = ( + "style" if self._heading_level_from_style(style_name) else "heuristic" + ) + return block + + # Detect caption (improved pattern matching) + caption_info = self._detect_caption(style_name, text) + if caption_info: + block["type"] = "caption" + block.update(caption_info) + return block + + # Reference paragraph + if in_reference_section: + block["type"] = "reference-paragraph" + ref_info = self._parse_reference_entry(text) + block.update(ref_info) + return block + + # List item detection + if self._is_list_item(para): + block["type"] = "list-item" + list_info = self._parse_list_item(para) + block.update(list_info) + return block + + # Normal paragraph + block["type"] = "paragraph" + return block + + def _detect_heading( + self, + para, + style_name: str, + text: str, + runs: List[Dict[str, Any]], + ) -> Optional[int]: + """ + Detect heading using multiple strategies: + 1. Style-based (most reliable) + 2. Font-based heuristics (bold, larger size) + 3. Content-based (known section titles) + """ + # Strategy 1: Style-based detection + level = self._heading_level_from_style(style_name) + if level is not None: + return level + + # Strategy 2: Font-based heuristics + # Check if entire paragraph is bold and short + text_clean = text.strip() + if len(text_clean) < 100: # Headings are typically short + all_bold = all(r.get("bold") for r in runs if r.get("text", "").strip()) + if all_bold and runs: + # Check font size - headings often larger + avg_size = self._get_average_font_size(runs) + if avg_size and avg_size >= 12: + # Check if it looks like a section heading + if self._looks_like_heading(text_clean): + return 1 if avg_size >= 14 else 2 + + # Strategy 3: Content-based detection (common section titles) + text_lower = text_clean.lower().rstrip(".:;") + # Check numbered sections: "1. Introduction", "2.1 Methods" + numbered_match = re.match(r"^(\d+(?:\.\d+)*)[\.:\s]+(.+)$", text_clean) + if numbered_match: + section_text = numbered_match.group(2).lower().strip() + if section_text in COMMON_SECTION_HEADINGS: + depth = numbered_match.group(1).count(".") + return min(depth + 1, 4) + + # Check unnumbered common headings (if bold or all caps) + if text_lower in COMMON_SECTION_HEADINGS: + is_bold = all(r.get("bold") for r in runs if r.get("text", "").strip()) + is_all_caps = text_clean.isupper() and len(text_clean) > 3 + if is_bold or is_all_caps: + return 1 + + return None + + def _looks_like_heading(self, text: str) -> bool: + """Check if text looks like a heading based on content patterns.""" + text_lower = text.lower().rstrip(".:;") + + # Check common section headings + if text_lower in COMMON_SECTION_HEADINGS: + return True + + # Check numbered sections + if re.match(r"^\d+(?:\.\d+)*\s+\w", text): + return True + + # All caps short text + if text.isupper() and 3 < len(text) < 50: + return True + + return False + + def _get_average_font_size(self, runs: List[Dict[str, Any]]) -> Optional[float]: + """Get average font size from runs.""" + sizes = [r["font_size"] for r in runs if r.get("font_size")] + return sum(sizes) / len(sizes) if sizes else None + + def _detect_caption(self, style_name: str, text: str) -> Optional[Dict[str, Any]]: + """ + Detect and parse captions using multiple patterns. + Returns caption info dict or None. + """ + # Check by style first + if style_name == self.profile.caption_style: + return self._parse_caption(text) + + # Check using comprehensive patterns + text_stripped = text.strip() + for pattern, caption_type in CAPTION_PATTERNS: + match = re.match(pattern, text_stripped, re.IGNORECASE) + if match: + return { + "caption_type": caption_type, + "number": int(match.group(2)), + "caption_text": match.group(3).strip(), + } + + # Check profile-specific prefixes + if self._is_caption(style_name, text): + return self._parse_caption(text) + + return None + + def _extract_equation(self, para) -> Optional[str]: + """ + Extract equation from paragraph if it contains OMML (Office Math Markup). + Returns LaTeX representation or None. + """ + try: + # Check for oMath elements + omml_ns = { + "m": "http://schemas.openxmlformats.org/officeDocument/2006/math" + } + math_elements = para._element.findall(".//m:oMath", namespaces=omml_ns) + + if not math_elements: + return None + + # Basic OMML to LaTeX conversion + latex_parts = [] + for math_elem in math_elements: + latex = self._omml_to_latex(math_elem) + if latex: + latex_parts.append(latex) + + return " ".join(latex_parts) if latex_parts else None + except Exception: + return None + + def _omml_to_latex(self, math_elem) -> str: + """ + Convert OMML element to LaTeX string. + This is a basic converter - handles common cases. + """ + omml_ns = {"m": "http://schemas.openxmlformats.org/officeDocument/2006/math"} + + def get_text(elem) -> str: + """Recursively get text from element.""" + texts = [] + if elem.text: + texts.append(elem.text) + for child in elem: + texts.append(get_text(child)) + if child.tail: + texts.append(child.tail) + return "".join(texts) + + def convert_element(elem) -> str: + """Convert a single OMML element to LaTeX.""" + tag = elem.tag.split("}")[-1] if "}" in elem.tag else elem.tag + + if tag == "r": # Run (text) + return get_text(elem) + elif tag == "f": # Fraction + num = elem.find("m:num", namespaces=omml_ns) + den = elem.find("m:den", namespaces=omml_ns) + num_tex = convert_children(num) if num is not None else "" + den_tex = convert_children(den) if den is not None else "" + return f"\\frac{{{num_tex}}}{{{den_tex}}}" + elif tag == "rad": # Radical/root + deg = elem.find("m:deg", namespaces=omml_ns) + content = elem.find("m:e", namespaces=omml_ns) + content_tex = convert_children(content) if content is not None else "" + if deg is not None and get_text(deg).strip(): + deg_tex = convert_children(deg) + return f"\\sqrt[{deg_tex}]{{{content_tex}}}" + return f"\\sqrt{{{content_tex}}}" + elif tag == "sSup": # Superscript + base = elem.find("m:e", namespaces=omml_ns) + sup = elem.find("m:sup", namespaces=omml_ns) + base_tex = convert_children(base) if base is not None else "" + sup_tex = convert_children(sup) if sup is not None else "" + return f"{base_tex}^{{{sup_tex}}}" + elif tag == "sSub": # Subscript + base = elem.find("m:e", namespaces=omml_ns) + sub = elem.find("m:sub", namespaces=omml_ns) + base_tex = convert_children(base) if base is not None else "" + sub_tex = convert_children(sub) if sub is not None else "" + return f"{base_tex}_{{{sub_tex}}}" + elif tag == "sSubSup": # Sub-superscript + base = elem.find("m:e", namespaces=omml_ns) + sub = elem.find("m:sub", namespaces=omml_ns) + sup = elem.find("m:sup", namespaces=omml_ns) + base_tex = convert_children(base) if base is not None else "" + sub_tex = convert_children(sub) if sub is not None else "" + sup_tex = convert_children(sup) if sup is not None else "" + return f"{base_tex}_{{{sub_tex}}}^{{{sup_tex}}}" + elif tag == "nary": # N-ary (sum, product, integral) + chr_elem = elem.find(".//m:chr", namespaces=omml_ns) + symbol = chr_elem.get(qn("m:val")) if chr_elem is not None else "∑" + symbol_map = {"∑": "\\sum", "∏": "\\prod", "∫": "\\int", "∮": "\\oint"} + latex_sym = symbol_map.get(symbol, symbol) + sub = elem.find("m:sub", namespaces=omml_ns) + sup = elem.find("m:sup", namespaces=omml_ns) + content = elem.find("m:e", namespaces=omml_ns) + result = latex_sym + if sub is not None: + result += f"_{{{convert_children(sub)}}}" + if sup is not None: + result += f"^{{{convert_children(sup)}}}" + if content is not None: + result += f" {convert_children(content)}" + return result + elif tag == "d": # Delimiter (parentheses, brackets) + content = elem.find("m:e", namespaces=omml_ns) + content_tex = convert_children(content) if content is not None else "" + beg = elem.find(".//m:begChr", namespaces=omml_ns) + end = elem.find(".//m:endChr", namespaces=omml_ns) + left = beg.get(qn("m:val")) if beg is not None else "(" + right = end.get(qn("m:val")) if end is not None else ")" + return f"\\left{left}{content_tex}\\right{right}" + elif tag in ("e", "num", "den", "sub", "sup", "deg"): + # Container elements - just process children + return convert_children(elem) + else: + # Unknown element - try to get text + return convert_children(elem) + + def convert_children(elem) -> str: + """Convert all children of an element.""" + if elem is None: + return "" + parts = [] + for child in elem: + parts.append(convert_element(child)) + return "".join(parts) + + return convert_element(math_elem) + + def _is_list_item(self, para) -> bool: + """Check if paragraph is a list item.""" + try: + # Check for numbering properties + pPr = para._element.find(qn("w:pPr")) + if pPr is not None: + numPr = pPr.find(qn("w:numPr")) + if numPr is not None: + return True + + # Check for bullet/number at start of text + text = para.text.strip() + if re.match(r"^[\u2022\u2023\u25E6\u2043\u2219•‣◦⁃∙]\s", text): + return True + if re.match( + r"^(\d+[\.\):]|\([a-z]\)|\([ivxlc]+\)|[a-z][\.\)])\s", + text, + re.IGNORECASE, + ): + return True + + return False + except Exception: + return False + + def _parse_list_item(self, para) -> Dict[str, Any]: + """Parse list item to extract level and content.""" + info: Dict[str, Any] = {"list_type": "unordered", "level": 0} + + try: + pPr = para._element.find(qn("w:pPr")) + if pPr is not None: + numPr = pPr.find(qn("w:numPr")) + if numPr is not None: + ilvl = numPr.find(qn("w:ilvl")) + if ilvl is not None: + info["level"] = int(ilvl.get(qn("w:val"), 0)) + + # Detect ordered vs unordered + text = para.text.strip() + if re.match(r"^\d+[\.\):]\s", text): + info["list_type"] = "ordered" + except Exception: + pass + + return info + + def _extract_runs(self, para) -> List[Dict[str, Any]]: + """Extract formatted runs from a paragraph.""" + runs = [] + for run in para.runs: + if not run.text: + continue + run_data = { + "text": run.text, + "bold": run.bold, + "italic": run.italic, + "underline": run.underline is not None, + } + if run.font.size: + run_data["font_size"] = run.font.size.pt + if run.font.name: + run_data["font_name"] = run.font.name + runs.append(run_data) + return runs + + def _heading_level_from_style(self, style_name: str) -> Optional[int]: + """Return heading level for a given Word style, or None.""" + for level, expected_style in self.profile.heading_styles.items(): + if style_name == expected_style: + return level + return None + + def _is_caption(self, style_name: str, text: str) -> bool: + """Check if paragraph is a caption.""" + if style_name == self.profile.caption_style: + return True + + # Check by prefix + text_lower = text.lower() + prefixes = ( + self.profile.figure_caption_prefixes + self.profile.table_caption_prefixes + ) + for prefix in prefixes: + if text_lower.startswith(prefix.lower()): + return True + return False + + def _parse_caption(self, text: str) -> Dict[str, Any]: + """Parse caption text to extract figure/table number.""" + info: Dict[str, Any] = {} + + # Check figure + for prefix in self.profile.figure_caption_prefixes: + pattern = rf"^{re.escape(prefix)}\.?\s*(\d+)[\.:]?\s*(.*)$" + match = re.match(pattern, text, re.IGNORECASE) + if match: + info["caption_type"] = "figure" + info["number"] = int(match.group(1)) + info["caption_text"] = match.group(2).strip() + return info + + # Check table + for prefix in self.profile.table_caption_prefixes: + pattern = rf"^{re.escape(prefix)}\.?\s*(\d+)[\.:]?\s*(.*)$" + match = re.match(pattern, text, re.IGNORECASE) + if match: + info["caption_type"] = "table" + info["number"] = int(match.group(1)) + info["caption_text"] = match.group(2).strip() + return info + + info["caption_type"] = "unknown" + info["caption_text"] = text + return info + + def _parse_reference_entry(self, text: str) -> Dict[str, Any]: + """Parse a reference entry to extract citation number.""" + info: Dict[str, Any] = {} + + # Try to extract numbered reference: [1], 1., (1), etc. + patterns = [ + r"^\[(\d+)\]", # [1] Author... + r"^(\d+)\.", # 1. Author... + r"^\((\d+)\)", # (1) Author... + ] + for pattern in patterns: + match = re.match(pattern, text) + if match: + info["ref_number"] = int(match.group(1)) + info["ref_text"] = re.sub(pattern, "", text).strip() + break + else: + info["ref_text"] = text + + return info + + def _process_table( + self, + table, + block_index: int, + ) -> Dict[str, Any]: + """Process a table.""" + rows = [] + for row in table.rows: + cells = [] + for cell in row.cells: + cells.append(cell.text.strip()) + rows.append(cells) + + return { + "index": block_index, + "type": "table", + "rows": rows, + "num_rows": len(rows), + "num_cols": len(rows[0]) if rows else 0, + } + + def _extract_images( + self, + doc: DocxDocument, + source_path: Path, + ) -> List[Dict[str, Any]]: + """Extract embedded images from the document.""" + images = [] + + try: + for rel_id, rel in doc.part.rels.items(): + if "image" in rel.reltype: + image_part = rel.target_part + image_bytes = image_part.blob + + # Generate hash for deduplication + image_hash = hashlib.md5(image_bytes).hexdigest()[:12] + + # Determine extension from content type + content_type = image_part.content_type + ext_map = { + "image/png": ".png", + "image/jpeg": ".jpg", + "image/gif": ".gif", + "image/tiff": ".tiff", + "image/bmp": ".bmp", + } + ext = ext_map.get(content_type, ".png") + + images.append( + { + "rel_id": rel_id, + "hash": image_hash, + "content_type": content_type, + "extension": ext, + "size_bytes": len(image_bytes), + "data": image_bytes, # Raw bytes + } + ) + except Exception as e: + pass # Image extraction is optional + + return images + + def _parse_references( + self, + blocks: List[Dict[str, Any]], + ) -> List[Dict[str, Any]]: + """Extract and structure references from blocks.""" + references = [] + for block in blocks: + if block.get("type") == "reference-paragraph": + ref_entry = { + "number": block.get("ref_number"), + "text": block.get("ref_text", block.get("text", "")), + "raw": block.get("text", ""), + } + references.append(ref_entry) + return references
+ + + +__all__ = ["WordReader"] +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_msword/track_changes.html b/src/scitex/_sphinx_html/_modules/scitex_msword/track_changes.html new file mode 100644 index 000000000..b399e7674 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_msword/track_changes.html @@ -0,0 +1,1160 @@ + + + + + + + + scitex_msword.track_changes — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_msword.track_changes

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Timestamp: 2026-06-02 00:00:00
+# File: src/scitex_msword/track_changes.py
+#
+# Part of scitex-msword (AGPL-3.0-only). See LICENSE at the repo root.
+
+"""
+Track-Changes (revision) utilities for python-docx Documents.
+
+This module surfaces the OOXML revision primitives so agents can:
+
+1. Toggle Word's "Track Changes" switch (``<w:trackChanges/>`` in
+   ``word/settings.xml``) via :func:`enable_track_changes`.
+2. Wrap agent edits as ``<w:ins>`` / ``<w:del>`` revisions
+   (:func:`wrap_as_tracked_insertion`, :func:`wrap_as_tracked_deletion`).
+3. Extract all tracked changes (:func:`extract_tracked_changes`).
+4. Accept / reject all changes in bulk
+   (:func:`accept_all_tracked_changes`, :func:`reject_all_tracked_changes`).
+
+OOXML refs: ``w:trackChanges`` (ECMA-376 §17.15.1.86), ``w:ins``
+(§17.13.5.18), ``w:del`` (§17.13.5.14), ``w:delText`` (§17.13.5.15).
+"""
+
+from __future__ import annotations
+
+from datetime import datetime, timezone
+from typing import Any, List, Optional, Sequence
+
+try:
+    from docx.document import Document as DocxDocument  # type: ignore[import-untyped]
+    from docx.oxml.ns import qn  # type: ignore[import-untyped]
+    from docx.text.paragraph import Paragraph  # type: ignore[import-untyped]
+    from docx.text.run import Run  # type: ignore[import-untyped]
+    from lxml import etree
+
+    DOCX_AVAILABLE = True
+    _DOCX_IMPORT_ERROR: Optional[Exception] = None
+except ImportError as exc:  # pragma: no cover
+    DOCX_AVAILABLE = False
+    _DOCX_IMPORT_ERROR = exc
+    DocxDocument = None  # type: ignore[assignment,misc]
+    Paragraph = None  # type: ignore[assignment,misc]
+    Run = None  # type: ignore[assignment,misc]
+    qn = None  # type: ignore[assignment]
+    etree = None  # type: ignore[assignment]
+
+
+_W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
+
+
+def _ensure_docx_available() -> None:
+    if not DOCX_AVAILABLE:
+        raise ImportError(
+            "python-docx (and lxml) are required for scitex_msword.track_changes. "
+            "Install via `pip install python-docx`."
+        ) from _DOCX_IMPORT_ERROR
+
+
+# ---------------------------------------------------------------------------
+# Internal helpers
+# ---------------------------------------------------------------------------
+
+
+def _settings_element(document: "DocxDocument"):
+    """Return the lxml ``<w:settings>`` element for the document."""
+    return document.settings.element
+
+
+def _now_iso() -> str:
+    """UTC ISO-8601 timestamp at second precision (Word-friendly)."""
+    return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
+
+
+def _make_w_element(tag_local_name: str, **attrs):
+    """Create a ``w:<tag>`` lxml element with namespaced ``w:`` attributes."""
+    el = etree.Element(f"{{{_W_NS}}}{tag_local_name}")
+    for key, value in attrs.items():
+        if value is None:
+            continue
+        el.set(f"{{{_W_NS}}}{key}", str(value))
+    return el
+
+
+def _scan_max_revision_id(document: "DocxDocument") -> int:
+    """Largest ``w:id`` currently used on a ``<w:ins>`` / ``<w:del>``."""
+    body = document.element.body
+    ins_tag = f"{{{_W_NS}}}ins"
+    del_tag = f"{{{_W_NS}}}del"
+    max_id = 0
+    for elem in body.iter():
+        if elem.tag in (ins_tag, del_tag):
+            raw = elem.get(qn("w:id"))
+            try:
+                cid = int(raw) if raw is not None else 0
+            except (TypeError, ValueError):
+                cid = 0
+            if cid > max_id:
+                max_id = cid
+    return max_id
+
+
+def _resolve_runs(
+    paragraph: "Paragraph",
+    runs: Sequence[Any],
+) -> List["Run"]:
+    """Resolve Run objects / run indices into a list of paragraph Runs."""
+    all_runs = list(paragraph.runs)
+    elems = [r._r for r in all_runs]
+    resolved: List[Run] = []
+    for item in runs:
+        if isinstance(item, int):
+            if 0 <= item < len(all_runs):
+                resolved.append(all_runs[item])
+        else:
+            elem = getattr(item, "_r", None) or getattr(item, "element", None)
+            if elem is not None and elem in elems:
+                resolved.append(item)
+    return resolved
+
+
+def _wrap_runs_in_element(
+    paragraph: "Paragraph",
+    target_runs: Sequence["Run"],
+    wrapper_tag: str,
+    attrs: dict,
+):
+    """Wrap each run's ``<w:r>`` in a new ``<w:wrapper_tag>`` parent."""
+    wrappers = []
+    for run in target_runs:
+        r_elem = run._r
+        parent = r_elem.getparent()
+        if parent is None:
+            continue
+        idx = parent.index(r_elem)
+        wrapper = _make_w_element(wrapper_tag, **attrs)
+        parent.insert(idx, wrapper)
+        parent.remove(r_elem)
+        wrapper.append(r_elem)
+        wrappers.append(wrapper)
+    return wrappers
+
+
+def _next_revision_id(paragraph: "Paragraph", explicit: Optional[int]) -> int:
+    """Resolve the ``w:id`` for a new revision, defaulting to max+1."""
+    if explicit is not None:
+        return int(explicit)
+    try:
+        document = paragraph.part.document  # type: ignore[attr-defined]
+    except Exception:
+        document = None
+    if document is None:
+        return 1
+    return _scan_max_revision_id(document) + 1
+
+
+# ---------------------------------------------------------------------------
+# API 1: enable_track_changes / is_track_changes_enabled
+# ---------------------------------------------------------------------------
+
+
+
+[docs] +def enable_track_changes( + document: "DocxDocument", + enabled: bool = True, +) -> "DocxDocument": + """ + Toggle Word's "Track Changes" switch on the document. + + Inserts ``<w:trackChanges/>`` into ``word/settings.xml`` when + ``enabled=True`` (idempotent) or removes it when ``enabled=False``. + + Parameters + ---------- + document : docx.Document + The Document to mutate in place. + enabled : bool, default True + ``True`` keeps a single ``<w:trackChanges/>`` element present; + ``False`` removes any such elements. + + Returns + ------- + docx.Document + The same Document object (chainable). + """ + _ensure_docx_available() + settings_el = _settings_element(document) + existing = settings_el.findall(qn("w:trackChanges")) + + if enabled: + if not existing: + settings_el.append(_make_w_element("trackChanges")) + else: + for dup in existing[1:]: + settings_el.remove(dup) + else: + for el in existing: + settings_el.remove(el) + return document
+ + + +
+[docs] +def is_track_changes_enabled(document: "DocxDocument") -> bool: + """Return True iff ``<w:trackChanges/>`` is present in settings.xml.""" + _ensure_docx_available() + return _settings_element(document).find(qn("w:trackChanges")) is not None
+ + + +# --------------------------------------------------------------------------- +# API 2: wrap_as_tracked_insertion +# --------------------------------------------------------------------------- + + +
+[docs] +def wrap_as_tracked_insertion( + paragraph: "Paragraph", + runs: Sequence[Any], + author: str = "agent", + date: Optional[str] = None, + w_id: Optional[int] = None, +) -> List[Any]: + """ + Wrap the given runs of ``paragraph`` in ``<w:ins>`` revision blocks. + + Word renders the wrapped content as "inserted by <author>" and + surfaces it as an accept/reject-able revision. + + Parameters + ---------- + paragraph : docx.text.paragraph.Paragraph + Paragraph that owns the runs to wrap. + runs : sequence of Run or int + Runs to wrap, by Run object or by 0-based index. + author : str, default "agent" + Recorded in ``w:author``. + date : str, optional + ISO-8601 string for ``w:date``; defaults to ``now(UTC)``. + w_id : int, optional + Explicit revision id; auto-assigned (max+1) when ``None``. + + Returns + ------- + list + Newly created ``<w:ins>`` lxml elements. + """ + _ensure_docx_available() + target_runs = _resolve_runs(paragraph, runs) + if not target_runs: + return [] + + attrs = { + "id": _next_revision_id(paragraph, w_id), + "author": author, + "date": date or _now_iso(), + } + return _wrap_runs_in_element(paragraph, target_runs, "ins", attrs)
+ + + +# --------------------------------------------------------------------------- +# API 3: wrap_as_tracked_deletion +# --------------------------------------------------------------------------- + + +
+[docs] +def wrap_as_tracked_deletion( + paragraph: "Paragraph", + runs: Sequence[Any], + author: str = "agent", + date: Optional[str] = None, + w_id: Optional[int] = None, +) -> List[Any]: + """ + Wrap the given runs of ``paragraph`` in ``<w:del>`` revision blocks. + + Each wrapped run's ``<w:t>`` children are also retagged as + ``<w:delText>`` so Word renders the deletion with strike-through. + + Parameters + ---------- + paragraph : docx.text.paragraph.Paragraph + Paragraph that owns the runs to wrap. + runs : sequence of Run or int + Runs to wrap, by Run object or by 0-based index. + author : str, default "agent" + Recorded in ``w:author``. + date : str, optional + ISO-8601 string for ``w:date``; defaults to ``now(UTC)``. + w_id : int, optional + Explicit revision id; auto-assigned (max+1) when ``None``. + + Returns + ------- + list + Newly created ``<w:del>`` lxml elements. + """ + _ensure_docx_available() + target_runs = _resolve_runs(paragraph, runs) + if not target_runs: + return [] + + attrs = { + "id": _next_revision_id(paragraph, w_id), + "author": author, + "date": date or _now_iso(), + } + wrappers = _wrap_runs_in_element(paragraph, target_runs, "del", attrs) + + t_qn = qn("w:t") + delText_qn = qn("w:delText") + for wrapper in wrappers: + for t in list(wrapper.iter(t_qn)): + t.tag = delText_qn + return wrappers
+ + + +# --------------------------------------------------------------------------- +# API 4: extract_tracked_changes +# --------------------------------------------------------------------------- + + +
+[docs] +def extract_tracked_changes( + document: "DocxDocument", +) -> List[dict]: + """ + Return every ``<w:ins>`` / ``<w:del>`` revision as a structured dict. + + Parameters + ---------- + document : docx.Document + The Document to scan. + + Returns + ------- + list[dict] + Each entry is shaped as:: + + {"type": "insert" | "delete", + "paragraph_idx": int, + "author": str, + "date": str, + "id": str, + "text": str} + """ + _ensure_docx_available() + ins_tag = f"{{{_W_NS}}}ins" + del_tag = f"{{{_W_NS}}}del" + t_tag = f"{{{_W_NS}}}t" + delText_tag = f"{{{_W_NS}}}delText" + id_attr = qn("w:id") + author_attr = qn("w:author") + date_attr = qn("w:date") + + results: List[dict] = [] + for pi, para in enumerate(document.paragraphs): + for elem in para._p.iter(): + if elem.tag not in (ins_tag, del_tag): + continue + texts: List[str] = [] + for t in elem.iter(): + if t.tag in (t_tag, delText_tag) and t.text: + texts.append(t.text) + results.append( + { + "type": "insert" if elem.tag == ins_tag else "delete", + "paragraph_idx": pi, + "author": elem.get(author_attr, ""), + "date": elem.get(date_attr, ""), + "id": elem.get(id_attr, ""), + "text": "".join(texts), + } + ) + return results
+ + + +# --------------------------------------------------------------------------- +# API 5: accept_all / reject_all +# --------------------------------------------------------------------------- + + +def _unwrap_element(elem) -> None: + """Replace ``elem`` in its parent with its own children, in order.""" + parent = elem.getparent() + if parent is None: + return + idx = parent.index(elem) + for offset, child in enumerate(list(elem)): + elem.remove(child) + parent.insert(idx + offset, child) + parent.remove(elem) + + +
+[docs] +def accept_all_tracked_changes(document: "DocxDocument") -> "DocxDocument": + """ + Accept all tracked changes — equivalent to Word's "Accept All". + + ``<w:ins>`` wrappers are unwrapped (content remains); ``<w:del>`` + wrappers and their contents are removed. + + Parameters + ---------- + document : docx.Document + The Document to mutate in place. + + Returns + ------- + docx.Document + The same Document, mutated. + """ + _ensure_docx_available() + body = document.element.body + ins_tag = f"{{{_W_NS}}}ins" + del_tag = f"{{{_W_NS}}}del" + + for el in [e for e in body.iter() if e.tag in (ins_tag, del_tag)]: + parent = el.getparent() + if parent is None: + continue + if el.tag == ins_tag: + _unwrap_element(el) + else: + parent.remove(el) + return document
+ + + +
+[docs] +def reject_all_tracked_changes(document: "DocxDocument") -> "DocxDocument": + """ + Reject all tracked changes — equivalent to Word's "Reject All". + + ``<w:ins>`` wrappers and contents are removed; ``<w:del>`` wrappers + are unwrapped and their ``<w:delText>`` children retagged back to + ``<w:t>`` so the original text is restored. + + Parameters + ---------- + document : docx.Document + The Document to mutate in place. + + Returns + ------- + docx.Document + The same Document, mutated. + """ + _ensure_docx_available() + body = document.element.body + ins_tag = f"{{{_W_NS}}}ins" + del_tag = f"{{{_W_NS}}}del" + t_tag = qn("w:t") + delText_tag = f"{{{_W_NS}}}delText" + + for el in [e for e in body.iter() if e.tag in (ins_tag, del_tag)]: + parent = el.getparent() + if parent is None: + continue + if el.tag == ins_tag: + parent.remove(el) + else: + for dt in list(el.iter(delText_tag)): + dt.tag = t_tag + _unwrap_element(el) + return document
+ + + +__all__ = [ + "enable_track_changes", + "is_track_changes_enabled", + "wrap_as_tracked_insertion", + "wrap_as_tracked_deletion", + "extract_tracked_changes", + "accept_all_tracked_changes", + "reject_all_tracked_changes", +] +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_msword/utils.html b/src/scitex/_sphinx_html/_modules/scitex_msword/utils.html new file mode 100644 index 000000000..8195ec24c --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_msword/utils.html @@ -0,0 +1,986 @@ + + + + + + + + scitex_msword.utils — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_msword.utils

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Timestamp: 2025-12-11 16:45:00
+# File: /home/ywatanabe/proj/scitex-code/src/scitex/msword/utils.py
+
+"""
+Utility functions for processing MS Word documents.
+
+These functions can be used as post_import_hooks or called directly
+to process document structures.
+"""
+
+from __future__ import annotations
+
+from typing import Any, Dict, List
+
+
+
+
+
+
+
+
+
+
+
+[docs] +def normalize_section_headings(doc: Dict[str, Any]) -> Dict[str, Any]: + """ + Normalize section headings for consistency. + + Converts common section titles to standard academic format: + - "intro" -> "Introduction" + - "method" -> "Methods" + - etc. + + Parameters + ---------- + doc : dict + SciTeX writer document. + + Returns + ------- + dict + Document with normalized headings. + """ + blocks = doc.get("blocks", []) + + # Common normalizations + normalizations = { + "intro": "Introduction", + "introduction": "Introduction", + "method": "Methods", + "methods": "Methods", + "materials and methods": "Materials and Methods", + "result": "Results", + "results": "Results", + "discussion": "Discussion", + "conclusion": "Conclusions", + "conclusions": "Conclusions", + "acknowledgement": "Acknowledgements", + "acknowledgements": "Acknowledgements", + "reference": "References", + "references": "References", + "bibliography": "References", + } + + for block in blocks: + if block.get("type") == "heading" and block.get("level") == 1: + text = block.get("text", "").strip().lower() + if text in normalizations: + block["text"] = normalizations[text] + + return doc
+ + + +
+[docs] +def validate_document(doc: Dict[str, Any]) -> Dict[str, Any]: + """ + Validate document structure and add warnings. + + Checks for common issues: + - Missing required sections + - Unmatched caption numbers + - Empty references section + - Duplicate figure numbers + + Parameters + ---------- + doc : dict + SciTeX writer document. + + Returns + ------- + dict + Document with warnings added. + """ + blocks = doc.get("blocks", []) + warnings = doc.get("warnings", []) + + # Check for required sections + headings = [b.get("text", "").lower() for b in blocks if b.get("type") == "heading"] + + required_sections = [ + "introduction", + "methods", + "results", + "discussion", + "references", + ] + for section in required_sections: + if not any(section in h for h in headings): + warnings.append(f"Missing section: {section.title()}") + + # Check for duplicate figure numbers + figure_numbers = [ + b.get("number") + for b in blocks + if b.get("type") == "caption" and b.get("caption_type") == "figure" + ] + seen = set() + for num in figure_numbers: + if num in seen: + warnings.append(f"Duplicate figure number: {num}") + seen.add(num) + + # Check for missing references + references = doc.get("references", []) + if not references: + ref_blocks = [b for b in blocks if b.get("type") == "reference-paragraph"] + if not ref_blocks: + warnings.append("No references found in document") + + doc["warnings"] = warnings + return doc
+ + + +
+[docs] +def create_post_import_hook(*functions): + """ + Create a composite post_import_hook from multiple functions. + + Parameters + ---------- + *functions : callable + Functions to apply in sequence. + + Returns + ------- + callable + A single hook that applies all functions. + + Examples + -------- + >>> from scitex.msword.utils import ( + ... link_captions_to_images, + ... normalize_section_headings, + ... create_post_import_hook, + ... ) + >>> hook = create_post_import_hook( + ... link_captions_to_images, + ... normalize_section_headings, + ... ) + >>> # Use with custom profile + >>> profile.post_import_hooks = [hook] + """ + + def composite_hook(doc: Dict[str, Any]) -> Dict[str, Any]: + for func in functions: + doc = func(doc) + return doc + + return composite_hook
+ + + +__all__ = [ + "link_captions_to_images", + "link_captions_to_images_by_proximity", + "normalize_section_headings", + "validate_document", + "create_post_import_hook", +] +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_msword/writer.html b/src/scitex/_sphinx_html/_modules/scitex_msword/writer.html new file mode 100644 index 000000000..958c6b9ef --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_msword/writer.html @@ -0,0 +1,1043 @@ + + + + + + + + scitex_msword.writer — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_msword.writer

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Timestamp: 2025-12-11 15:15:00
+# File: /home/ywatanabe/proj/scitex-code/src/scitex/msword/writer.py
+
+"""
+SciTeX writer document -> DOCX converter.
+
+This module exports SciTeX documents to MS Word .docx files,
+applying journal-specific styles and formatting.
+"""
+
+from __future__ import annotations
+
+from pathlib import Path
+from typing import Any, Dict, List, Optional
+
+from .profiles import BaseWordProfile
+
+# Lazy import for python-docx
+try:
+    import docx
+    from docx.document import Document as DocxDocument
+    from docx.enum.style import WD_STYLE_TYPE
+    from docx.enum.text import WD_ALIGN_PARAGRAPH
+    from docx.shared import Cm, Inches, Pt
+
+    DOCX_AVAILABLE = True
+    _DOCX_IMPORT_ERROR = None
+except ImportError as exc:
+    DOCX_AVAILABLE = False
+    _DOCX_IMPORT_ERROR = exc
+
+
+
+[docs] +class WordWriter: + """ + Export a SciTeX writer document to a DOCX file. + + This writer handles: + - Section headings with proper styles + - Paragraphs with formatting + - Figure and table captions + - References section + - Image embedding + - Journal-specific template application + """ + +
+[docs] + def __init__( + self, + profile: BaseWordProfile, + template_path: Optional[Path] = None, + ): + """ + Parameters + ---------- + profile : BaseWordProfile + Mapping from writer structures to Word styles. + template_path : Path | None + Optional path to a Word template (.dotx/.docx) to use as base. + """ + if not DOCX_AVAILABLE: + raise ImportError( + "python-docx is required for scitex.msword.WordWriter. " + "Install it via `pip install python-docx`." + ) from _DOCX_IMPORT_ERROR + self.profile = profile + self.template_path = template_path
+ + +
+[docs] + def write( + self, + writer_doc: Dict[str, Any] | Any, + path: Path, + ) -> None: + """ + Write a SciTeX writer document to a DOCX file. + + Parameters + ---------- + writer_doc : dict | Any + Writer document or intermediate structure. + path : Path + Output path for the DOCX file. + """ + # Create document (from template if specified) + if self.template_path and Path(self.template_path).exists(): + doc = docx.Document(str(self.template_path)) + # Clear existing content but keep styles + self._clear_document_content(doc) + else: + doc = docx.Document() + + # Run pre-export hooks + for hook in self.profile.pre_export_hooks: + writer_doc = hook(writer_doc) + + # Extract blocks from writer_doc + if isinstance(writer_doc, dict) and "blocks" in writer_doc: + blocks = writer_doc["blocks"] + images = writer_doc.get("images", []) + else: + blocks = list(writer_doc) + images = [] + + # Build image lookup by hash + image_lookup = {img.get("hash"): img for img in images if "hash" in img} + + # Process each block + for block in blocks: + self._add_block(doc, block, image_lookup) + + # Apply double-anonymous processing if needed + if self.profile.double_anonymous: + self._apply_double_anonymous(doc, writer_doc) + + # Save document + doc.save(str(path))
+ + + def _clear_document_content(self, doc: DocxDocument) -> None: + """Clear document content while preserving styles.""" + for element in doc.element.body[:]: + doc.element.body.remove(element) + + def _add_block( + self, + doc: DocxDocument, + block: Dict[str, Any], + image_lookup: Dict[str, Any], + ) -> None: + """Add a single block to the document.""" + btype = block.get("type", "paragraph") + text = block.get("text", "") + + if not text and btype not in ("table", "image"): + return + + if btype == "heading": + level = block.get("level", 1) + self._add_heading(doc, text, level) + + elif btype == "caption": + self._add_caption(doc, block) + + elif btype == "reference-paragraph": + self._add_reference(doc, block) + + elif btype == "table": + self._add_table(doc, block) + + elif btype == "image": + self._add_image(doc, block, image_lookup) + + elif btype == "list-item": + self._add_list_item(doc, block) + + else: + # Default: paragraph + self._add_paragraph(doc, text, block.get("runs")) + + def _add_heading( + self, + doc: DocxDocument, + text: str, + level: int, + ) -> None: + """Add a heading paragraph at the given logical level.""" + style_name = self.profile.heading_styles.get(level) + + if style_name and self._style_exists(doc, style_name): + p = doc.add_paragraph(text) + p.style = style_name + else: + # Fallback to built-in heading + doc.add_heading(text, level=min(level, 9)) + + def _add_paragraph( + self, + doc: DocxDocument, + text: str, + runs: Optional[List[Dict[str, Any]]] = None, + ) -> None: + """Add a paragraph with optional formatted runs.""" + p = doc.add_paragraph() + + if runs: + # Add formatted runs + for run_data in runs: + run = p.add_run(run_data.get("text", "")) + if run_data.get("bold"): + run.bold = True + if run_data.get("italic"): + run.italic = True + if run_data.get("underline"): + run.underline = True + if run_data.get("font_size"): + run.font.size = Pt(run_data["font_size"]) + if run_data.get("font_name"): + run.font.name = run_data["font_name"] + else: + p.add_run(text) + + # Apply normal style + if self._style_exists(doc, self.profile.normal_style): + try: + p.style = self.profile.normal_style + except Exception: + pass + + def _add_caption( + self, + doc: DocxDocument, + block: Dict[str, Any], + ) -> None: + """Add a figure or table caption.""" + caption_type = block.get("caption_type", "") + number = block.get("number", "") + caption_text = block.get("caption_text", block.get("text", "")) + + # Build caption text + if caption_type == "figure" and number: + full_text = f"Figure {number}. {caption_text}" + elif caption_type == "table" and number: + full_text = f"Table {number}. {caption_text}" + else: + full_text = block.get("text", caption_text) + + p = doc.add_paragraph(full_text) + + if self._style_exists(doc, self.profile.caption_style): + try: + p.style = self.profile.caption_style + except Exception: + pass + + def _add_reference( + self, + doc: DocxDocument, + block: Dict[str, Any], + ) -> None: + """Add a reference entry.""" + ref_number = block.get("ref_number") + ref_text = block.get("ref_text", block.get("text", "")) + + if ref_number is not None: + full_text = f"[{ref_number}] {ref_text}" + else: + full_text = ref_text + + p = doc.add_paragraph(full_text) + + if self._style_exists(doc, self.profile.normal_style): + try: + p.style = self.profile.normal_style + except Exception: + pass + + def _add_table( + self, + doc: DocxDocument, + block: Dict[str, Any], + ) -> None: + """Add a table.""" + rows = block.get("rows", []) + if not rows: + return + + num_rows = len(rows) + num_cols = len(rows[0]) if rows else 0 + + table = doc.add_table(rows=num_rows, cols=num_cols) + table.style = "Table Grid" + + for i, row_data in enumerate(rows): + row = table.rows[i] + for j, cell_text in enumerate(row_data): + if j < len(row.cells): + row.cells[j].text = str(cell_text) + + def _add_image( + self, + doc: DocxDocument, + block: Dict[str, Any], + image_lookup: Dict[str, Any], + ) -> None: + """Add an image.""" + image_hash = block.get("image_hash") + image_data = block.get("data") + + if image_hash and image_hash in image_lookup: + image_info = image_lookup[image_hash] + image_data = image_info.get("data") + + if image_data: + from io import BytesIO + + image_stream = BytesIO(image_data) + width = block.get("width_inches", 5.0) + doc.add_picture(image_stream, width=Inches(width)) + + def _add_list_item( + self, + doc: DocxDocument, + block: Dict[str, Any], + ) -> None: + """Add a list item (bullet or numbered).""" + text = block.get("text", "") + list_type = block.get("list_type", "bullet") + + p = doc.add_paragraph(text) + + style_key = "bullet" if list_type == "bullet" else "numbered" + style_name = self.profile.list_styles.get(style_key) + + if style_name and self._style_exists(doc, style_name): + try: + p.style = style_name + except Exception: + pass + + def _style_exists(self, doc: DocxDocument, style_name: str) -> bool: + """Check if a style exists in the document.""" + try: + _ = doc.styles[style_name] + return True + except KeyError: + return False + + def _apply_double_anonymous( + self, + doc: DocxDocument, + writer_doc: Dict[str, Any], + ) -> None: + """ + Apply double-anonymous formatting. + + This removes or masks author-identifying information. + """ + # Get author info to mask + metadata = writer_doc.get("metadata", {}) + author = metadata.get("author", "") + + if not author: + return + + # Search and replace author names with placeholder + # This is a simple implementation; more sophisticated + # masking may be needed for real use + for para in doc.paragraphs: + if author.lower() in para.text.lower(): + for run in para.runs: + if author.lower() in run.text.lower(): + # Mask author name + import re + + run.text = re.sub( + re.escape(author), + "[Author]", + run.text, + flags=re.IGNORECASE, + )
+ + + +__all__ = ["WordWriter"] +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_notebook/_compile.html b/src/scitex/_sphinx_html/_modules/scitex_notebook/_compile.html new file mode 100644 index 000000000..2c3c3f201 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_notebook/_compile.html @@ -0,0 +1,914 @@ + + + + + + + + scitex_notebook._compile — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_notebook._compile

+#!/usr/bin/env python3
+"""Compile notebook execution history into a DAG from clew DB timestamps."""
+
+from __future__ import annotations
+
+import logging
+from dataclasses import dataclass, field
+from pathlib import Path
+from typing import Dict, List, Optional, Union
+
+logger = logging.getLogger(__name__)
+
+from ._verify import _get_runs_for_notebook
+
+
+
+[docs] +@dataclass +class CompiledNotebook: + """Result of compiling a notebook's execution history. + + Attributes + ---------- + notebook_path : str + Path to the source notebook. + execution_order : list of str + Session IDs in actual execution order (by timestamp). + dag : dict + Adjacency list: {session_id: [dependent_session_ids]}. + runs : list of dict + Run records sorted by execution time. + """ + + notebook_path: str + execution_order: List[str] = field(default_factory=list) + dag: Dict[str, List[str]] = field(default_factory=dict) + runs: List[Dict] = field(default_factory=list) + +
+[docs] + def to_script(self) -> str: + """Generate a .py script with sessions in DAG order.""" + topo_order = _topological_sort(self.dag, self.execution_order) + lines = [ + "#!/usr/bin/env python3", + '"""Auto-compiled from notebook execution history."""', + "", + "import scitex as stx", + "", + ] + + for idx, sid in enumerate(topo_order): + run = _find_run(self.runs, sid) + script_path = run.get("script_path", "") if run else "" + func_name = f"step_{idx:02d}_{_safe_name(sid)}" + + lines.append("") + lines.append("@stx.session") + lines.append(f"def {func_name}():") + lines.append(f' """Session: {sid}"""') + lines.append(f" # Original script: {script_path}") + lines.append(" pass") + lines.append("") + lines.append(f"{func_name}()") + lines.append("") + + return "\n".join(lines)
+ + +
+[docs] + def to_mermaid(self) -> str: + """Generate a Mermaid DAG diagram of execution flow.""" + lines = ["graph TD"] + for sid in self.execution_order: + run = _find_run(self.runs, sid) + label = _short_id(sid) + if run: + started = run.get("started_at", "")[:19] + label = f"{_short_id(sid)}<br/>{started}" + lines.append(f' {_mermaid_id(sid)}["{label}"]') + + for parent, children in self.dag.items(): + for child in children: + lines.append(f" {_mermaid_id(parent)} --> {_mermaid_id(child)}") + + return "\n".join(lines)
+
+ + + +
+[docs] +def compile_notebook(path: Union[str, Path]) -> CompiledNotebook: + """Compile a notebook's execution history into a DAG. + + Queries the clew DB for all sessions associated with this notebook, + sorts by timestamp, and builds a dependency DAG based on shared + input/output files. + + Parameters + ---------- + path : str or Path + Path to the .ipynb file. + + Returns + ------- + CompiledNotebook + Compiled execution history with DAG and execution order. + """ + from scitex_clew import get_db + + path = Path(path).resolve() + db = get_db() + runs = _get_runs_for_notebook(db, str(path)) + + if not runs: + return CompiledNotebook(notebook_path=str(path)) + + execution_order = [r["session_id"] for r in runs] + dag = _build_dag(runs, db) + + return CompiledNotebook( + notebook_path=str(path), + execution_order=execution_order, + dag=dag, + runs=runs, + )
+ + + +def _build_dag(runs: List[Dict], db) -> Dict[str, List[str]]: + """Build DAG from IO dependencies between sessions. + + Session A -> Session B if A produced a file that B consumed. + """ + dag: Dict[str, List[str]] = {r["session_id"]: [] for r in runs} + session_ids = {r["session_id"] for r in runs} + + # Map: output file -> producing session + output_producers: Dict[str, str] = {} + for run in runs: + outputs = db.get_file_hashes(run["session_id"], role="output") + for file_path in outputs: + output_producers[file_path] = run["session_id"] + + # For each session's inputs, find the producer + for run in runs: + inputs = db.get_file_hashes(run["session_id"], role="input") + for file_path in inputs: + producer = output_producers.get(file_path) + if producer and producer != run["session_id"]: + if producer in session_ids: + if run["session_id"] not in dag[producer]: + dag[producer].append(run["session_id"]) + + return dag + + +def _topological_sort( + dag: Dict[str, List[str]], fallback_order: List[str] +) -> List[str]: + """Topological sort of DAG, falling back to timestamp order. + + Detects cycles and warns if nodes are unreachable due to cycles. + """ + from collections import deque as _deque # noqa: STX-I007 + from warnings import warn as _warn # noqa: STX-I007 + + if not dag: + return fallback_order + + in_degree: Dict[str, int] = dict.fromkeys(dag, 0) + for children in dag.values(): + for child in children: + in_degree.setdefault(child, 0) + in_degree[child] += 1 + + # Kahn's algorithm with fallback_order as tiebreaker + order_map = {sid: i for i, sid in enumerate(fallback_order)} + queue = _deque( + sorted( + [n for n, d in in_degree.items() if d == 0], + key=lambda n: order_map.get(n, 999), + ) + ) + result = [] + + while queue: + node = queue.popleft() + result.append(node) + for child in dag.get(node, []): + in_degree[child] -= 1 + if in_degree[child] == 0: + queue.append(child) + # Re-sort for stable tiebreaking + tmp = sorted(queue, key=lambda n: order_map.get(n, 999)) + queue.clear() + queue.extend(tmp) + + # Detect cycle: nodes in DAG but not in result + all_nodes = set(in_degree.keys()) + sorted_nodes = set(result) + cyclic_nodes = all_nodes - sorted_nodes + if cyclic_nodes: + _warn( + f"Cyclic dependencies detected among sessions: " + f"{sorted(cyclic_nodes)}. These sessions will be appended " + f"in timestamp order.", + stacklevel=2, + ) + + # Add remaining nodes (cyclic or not in DAG) in fallback order + remaining = [sid for sid in fallback_order if sid not in sorted_nodes] + result.extend(remaining) + + return result + + +def _find_run(runs: List[Dict], session_id: str) -> Optional[Dict]: + """Find a run by session ID.""" + for r in runs: + if r["session_id"] == session_id: + return r + return None + + +def _safe_name(session_id: str) -> str: + """Convert session ID to a valid Python identifier fragment.""" + return session_id.replace("-", "_").replace(".", "_")[:20] + + +def _short_id(session_id: str) -> str: + """Shorten session ID for display.""" + return session_id[:20] if len(session_id) > 20 else session_id + + +def _mermaid_id(session_id: str) -> str: + """Convert session ID to valid Mermaid node ID.""" + return session_id.replace("-", "_").replace(".", "_") + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_notebook/_convert.html b/src/scitex/_sphinx_html/_modules/scitex_notebook/_convert.html new file mode 100644 index 000000000..221dc1a64 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_notebook/_convert.html @@ -0,0 +1,955 @@ + + + + + + + + scitex_notebook._convert — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_notebook._convert

+#!/usr/bin/env python3
+"""Convert Jupyter notebooks to SciTeX-compatible Python scripts."""
+
+from __future__ import annotations
+
+import re
+from pathlib import Path
+from typing import List, Union
+
+from ._compile import compile_notebook
+from ._parse import get_code_cells, parse_notebook
+
+# IPython magic patterns to strip
+_MAGIC_RE = re.compile(r"^\s*[%!].*$", re.MULTILINE)
+
+# Import statement pattern (matches: import x, import x as y, import x.y.z as w, from x import y)
+_IMPORT_RE = re.compile(
+    r"^(?:import\s+\S+(?:\s+as\s+\S+)?|from\s+\S+\s+import\s+.+)$", re.MULTILINE
+)
+
+# Common notebook patterns to convert to SciTeX equivalents
+_CONVERSIONS = [
+    # plt.show() → stx.io.save(fig, "figure.png")
+    (re.compile(r"plt\.show\(\)"), '# stx.io.save(fig, "figure.png")  # was: plt.show()'),
+    # plt.savefig("...") → stx.io.save(fig, "...")
+    (
+        re.compile(r'plt\.savefig\((["\'].*?["\'])\)'),
+        r"stx.io.save(fig, \1)  # was: plt.savefig",
+    ),
+    # df.to_csv("...") → stx.io.save(df, "...")
+    (
+        re.compile(r'(\w+)\.to_csv\((["\'].*?["\'])\)'),
+        r"stx.io.save(\1, \2)  # was: .to_csv",
+    ),
+    # pd.read_csv("...") → stx.io.load("...")
+    (
+        re.compile(r'pd\.read_csv\((["\'].*?["\'])\)'),
+        r'stx.io.load(\1)  # was: pd.read_csv',
+    ),
+    # np.save("...", arr) → stx.io.save(arr, "...")
+    (
+        re.compile(r'np\.save\((["\'].*?["\'])\s*,\s*(.+?)\)'),
+        r"stx.io.save(\2, \1)  # was: np.save",
+    ),
+    # np.load("...") → stx.io.load("...")
+    (
+        re.compile(r'np\.load\((["\'].*?["\'])\)'),
+        r'stx.io.load(\1)  # was: np.load',
+    ),
+]
+
+
+
+[docs] +def convert_notebook( + path: Union[str, Path], + output: Union[str, Path, None] = None, + order: str = "cell", + mode: str = "per_cell", +) -> str: + """Convert a .ipynb notebook to a .py script with @stx.session. + + Parameters + ---------- + path : str or Path + Path to the .ipynb file. + output : str or Path, optional + Output .py file path. If None, returns string only. + order : str + Cell ordering: "cell" (notebook order) or "dag" (execution order + from clew DB timestamps). + mode : str + Conversion mode: + - "per_cell": Each code cell becomes a separate @stx.session function (default). + - "unified": All cells merged into a single @stx.session main() function. + Markdown cells become comments, imports are hoisted, and common + notebook patterns (plt.show, pd.read_csv, etc.) are converted to + SciTeX equivalents (stx.io.save/load). + + Returns + ------- + str + The generated Python script content. + """ + path = Path(path) + + if mode == "unified": + script = _convert_unified(path) + elif order == "cell": + script = _convert_cell_order(path) + elif order == "dag": + script = _convert_dag_order(path) + else: + raise ValueError(f"Invalid order: {order!r}. Must be 'cell' or 'dag'.") + + if output is not None: + output = Path(output) + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text(script, encoding="utf-8") + + return script
+ + + +def _convert_unified(path: Path) -> str: + """Convert notebook into a single @stx.session main() function. + + - Markdown cells become block comments + - Imports are hoisted to module level + - Code is merged into main() body + - Common IO patterns are converted to stx.io equivalents + """ + all_cells = parse_notebook(path) + imports: List[str] = [] + body_lines: List[str] = [] + has_plt = False + + for cell in all_cells: + if cell["cell_type"] == "markdown": + # Convert markdown to comments + md_text = cell["source"].strip() + if not md_text: + continue + body_lines.append("") + for md_line in md_text.splitlines(): + body_lines.append(f" # {md_line}") + body_lines.append("") + + elif cell["cell_type"] == "code": + source = _clean_source(cell["source"]) + if not source.strip(): + continue + + # Separate imports from body code + cell_imports, cell_body = _separate_imports(source) + imports.extend(cell_imports) + + if cell_body.strip(): + # Apply SciTeX conversions + cell_body = _apply_conversions(cell_body) + body_lines.append("") + for line in cell_body.splitlines(): + body_lines.append(f" {line}" if line.strip() else "") + + # Track matplotlib usage + if "plt." in source or "matplotlib" in source: + has_plt = True + + # Deduplicate imports + seen = set() + unique_imports = [] + for imp in imports: + if imp not in seen: + seen.add(imp) + unique_imports.append(imp) + + # Filter out imports that stx.session provides (plt is injected) + filtered_imports = [ + imp + for imp in unique_imports + if not imp.startswith("import matplotlib") + and not imp.startswith("from matplotlib") + and "matplotlib.pyplot" not in imp + ] + + # Build script + lines = [ + "#!/usr/bin/env python3", + f'"""Converted from {path.name} using scitex notebook convert --mode unified."""', + "", + "import scitex as stx", + ] + + # Add non-scitex imports + for imp in filtered_imports: + if "scitex" not in imp: + lines.append(imp) + + lines.append("") + lines.append("") + + # Build injected parameters + injected = [" CONFIG=stx.INJECTED,", " logger=stx.INJECTED,"] + if has_plt: + injected.append(" plt=stx.INJECTED,") + + lines.append("@stx.session(seed=42)") + lines.append("def main(") + lines.extend(injected) + lines.append("):") + lines.append(f' """Analysis pipeline converted from {path.name}."""') + + # Add body + lines.extend(body_lines) + + lines.append("") + lines.append(" return 0") + lines.append("") + lines.append("") + lines.append('if __name__ == "__main__":') + lines.append(" main()") + lines.append("") + lines.append("# EOF") + lines.append("") + + return "\n".join(lines) + + +def _separate_imports(source: str) -> tuple: + """Separate import statements from body code.""" + imports = [] + body_lines = [] + + for line in source.splitlines(): + stripped = line.strip() + if _IMPORT_RE.match(stripped): + imports.append(stripped) + else: + body_lines.append(line) + + return imports, "\n".join(body_lines) + + +def _apply_conversions(source: str) -> str: + """Apply SciTeX pattern conversions to source code.""" + for pattern, replacement in _CONVERSIONS: + source = pattern.sub(replacement, source) + return source + + +def _convert_cell_order(path: Path) -> str: + """Convert notebook in cell index order.""" + cells = get_code_cells(path) + lines = _script_header(path) + + for cell in cells: + source = _clean_source(cell["source"]) + if not source.strip(): + continue + + idx = cell["index"] + func_name = f"cell_{idx:02d}" + + lines.append("") + lines.append("@stx.session") + lines.append(f"def {func_name}():") + for line in source.splitlines(): + lines.append(f" {line}" if line.strip() else "") + lines.append(" return 0") + lines.append("") + lines.append(f"{func_name}()") + lines.append("") + + return "\n".join(lines) + + +def _convert_dag_order(path: Path) -> str: + """Convert notebook in DAG execution order from clew DB.""" + compiled = compile_notebook(path) + + if not compiled.execution_order: + # No execution history; fall back to cell order + return _convert_cell_order(path) + + return compiled.to_script() + + +def _clean_source(source: str) -> str: + """Strip IPython magics and clean up source code.""" + return _MAGIC_RE.sub("", source) + + +def _script_header(path: Path) -> List[str]: + """Generate script header.""" + return [ + "#!/usr/bin/env python3", + f'"""Converted from {path.name}."""', + "", + "import scitex as stx", + "", + ] + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_notebook/_parse.html b/src/scitex/_sphinx_html/_modules/scitex_notebook/_parse.html new file mode 100644 index 000000000..e2847f373 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_notebook/_parse.html @@ -0,0 +1,756 @@ + + + + + + + + scitex_notebook._parse — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_notebook._parse

+#!/usr/bin/env python3
+"""Parse Jupyter notebook files using stdlib json (no nbformat dependency)."""
+
+from __future__ import annotations
+
+import json
+from pathlib import Path
+from typing import Dict, List, Union
+
+
+
+[docs] +def parse_notebook(path: Union[str, Path]) -> List[Dict]: + """Parse a .ipynb file and extract code cells. + + Parameters + ---------- + path : str or Path + Path to the .ipynb file. + + Returns + ------- + list of dict + Code cells with keys: index, source, cell_id, cell_type. + """ + path = Path(path) + if not path.exists(): + raise FileNotFoundError(f"Notebook not found: {path}") + if path.suffix != ".ipynb": + raise ValueError(f"Not a notebook file: {path}") + + with open(path, encoding="utf-8") as f: + nb = json.load(f) + + cells = nb.get("cells", []) + result = [] + for idx, cell in enumerate(cells): + cell_type = cell.get("cell_type", "") + source_lines = cell.get("source", []) + source = ( + "".join(source_lines) if isinstance(source_lines, list) else source_lines + ) + cell_id = cell.get("id", f"cell_{idx}") + + result.append( + { + "index": idx, + "source": source, + "cell_id": cell_id, + "cell_type": cell_type, + } + ) + + return result
+ + + +
+[docs] +def get_code_cells(path: Union[str, Path]) -> List[Dict]: + """Parse notebook and return only code cells. + + Parameters + ---------- + path : str or Path + Path to the .ipynb file. + + Returns + ------- + list of dict + Code cells only. + """ + return [c for c in parse_notebook(path) if c["cell_type"] == "code"]
+ + + +
+[docs] +def get_notebook_name(path: Union[str, Path]) -> str: + """Return the notebook stem name without extension.""" + return Path(path).stem
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_notebook/_verify.html b/src/scitex/_sphinx_html/_modules/scitex_notebook/_verify.html new file mode 100644 index 000000000..e94f5db8d --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_notebook/_verify.html @@ -0,0 +1,802 @@ + + + + + + + + scitex_notebook._verify — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_notebook._verify

+#!/usr/bin/env python3
+"""Verify notebook sessions and check for untracked IO."""
+
+from __future__ import annotations
+
+import json
+import re
+from pathlib import Path
+from typing import Dict, List, Union
+
+from ._parse import get_code_cells
+
+# Patterns for scitex.io calls
+_IO_LOAD_RE = re.compile(r"(?:scitex|stx)\.io\.load\s*\(")
+_IO_SAVE_RE = re.compile(r"(?:scitex|stx)\.io\.save\s*\(")
+_SESSION_RE = re.compile(r"@(?:scitex|stx)\.session")
+
+
+
+[docs] +def verify_notebook(path: Union[str, Path]) -> List[Dict]: + """Verify all clew sessions associated with a notebook. + + Finds all runs in the clew DB whose metadata contains this notebook's + path, then runs L1 (cache) verification on each. + + Parameters + ---------- + path : str or Path + Path to the .ipynb file. + + Returns + ------- + list of dict + Verification results per session. + """ + from scitex_clew import get_db, verify_run + + path = Path(path).resolve() + db = get_db() + runs = _get_runs_for_notebook(db, str(path)) + + results = [] + for run in runs: + try: + verification = verify_run(run["session_id"]) + results.append( + { + "session_id": run["session_id"], + "status": verification.status.value, + "is_verified": verification.is_verified, + "started_at": run.get("started_at"), + } + ) + except Exception as exc: + results.append( + { + "session_id": run["session_id"], + "status": "error", + "is_verified": False, + "error": str(exc), + } + ) + + return results
+ + + +
+[docs] +def check_notebook(path: Union[str, Path]) -> List[Dict]: + """Find cells with scitex.io calls not wrapped in @scitex.session. + + Parameters + ---------- + path : str or Path + Path to the .ipynb file. + + Returns + ------- + list of dict + Cells with untracked IO: {index, has_load, has_save, has_session}. + """ + cells = get_code_cells(path) + issues = [] + + for cell in cells: + source = cell["source"] + has_load = bool(_IO_LOAD_RE.search(source)) + has_save = bool(_IO_SAVE_RE.search(source)) + has_session = bool(_SESSION_RE.search(source)) + + if (has_load or has_save) and not has_session: + issues.append( + { + "index": cell["index"], + "has_load": has_load, + "has_save": has_save, + "has_session": has_session, + } + ) + + return issues
+ + + +def _get_runs_for_notebook(db, notebook_path: str) -> List[Dict]: + """Query clew DB for runs associated with a notebook path.""" + runs = db.list_runs(limit=1000) + result = [] + for run in runs: + meta_str = run.get("metadata") + if not meta_str: + # Also check script_path for notebook path + sp = run.get("script_path", "") + if sp and sp.endswith(".ipynb"): + if str(Path(sp).resolve()) == notebook_path: + result.append(run) + continue + try: + meta = json.loads(meta_str) + nb_path = meta.get("notebook_path") + if nb_path and str(Path(nb_path).resolve()) == notebook_path: + result.append(run) + except (json.JSONDecodeError, TypeError): + continue + + return sorted(result, key=lambda r: r.get("started_at", "")) + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_os/_check_host.html b/src/scitex/_sphinx_html/_modules/scitex_os/_check_host.html new file mode 100644 index 000000000..3a9b06e80 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_os/_check_host.html @@ -0,0 +1,710 @@ + + + + + + + + scitex_os._check_host — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_os._check_host

+#!/usr/bin/env python3
+# Time-stamp: "2024-11-02 13:43:36 (ywatanabe)"
+# File: /home/ywatanabe/proj/scitex-python/src/scitex/os/_check_host.py
+
+import socket
+import sys
+
+
+
+[docs] +def check_host(keyword): + """Check if the current hostname contains the given keyword.""" + return keyword in socket.gethostname()
+ + + +is_host = check_host + + +
+[docs] +def verify_host(keyword): + if is_host(keyword): + print(f"Host verification successed for keyword: {keyword}") + return + else: + print(f"Host verification failed for keyword: {keyword}") + sys.exit(1)
+ + + +if __name__ == "__main__": # pragma: no cover + # check_host("ywata") + verify_host("titan") + verify_host("ywata") + verify_host("crest") + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_os/_mv.html b/src/scitex/_sphinx_html/_modules/scitex_os/_mv.html new file mode 100644 index 000000000..9fd58540a --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_os/_mv.html @@ -0,0 +1,726 @@ + + + + + + + + scitex_os._mv — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_os._mv

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Time-stamp: "2024-04-06 09:00:45 (ywatanabe)"
+
+# import os
+# import shutil
+
+# def mv(src, tgt):
+#     successful = True
+#     os.makedirs(tgt, exist_ok=True)
+
+#     if os.path.isdir(src):
+#         # Iterate over the items in the directory
+#         for item in os.listdir(src):
+#             item_path = os.path.join(src, item)
+#             # Check if the item is a file
+#             if os.path.isfile(item_path):
+#                 try:
+#                     shutil.move(item_path, tgt)
+#                     print(f"\nMoved file from {item_path} to {tgt}")
+#                 except OSError as e:
+#                     print(f"\nError: {e}")
+#                     successful = False
+#             else:
+#                 print(f"\nSkipped directory {item_path}")
+#     else:
+#         # If src is a file, just move it
+#         try:
+#             shutil.move(src, tgt)
+#             print(f"\nMoved from {src} to {tgt}")
+#         except OSError as e:
+#             print(f"\nError: {e}")
+#             successful = False
+
+#     return successful
+
+
+
+[docs] +def mv(src, tgt): + import os + import shutil + + successful = True + os.makedirs(tgt, exist_ok=True) + + try: + shutil.move(src, tgt) + print(f"\nMoved from {src} to {tgt}") + except OSError as e: + print(f"\nError: {e}") + successful = False + + return successful
+ +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_parallel/_run.html b/src/scitex/_sphinx_html/_modules/scitex_parallel/_run.html new file mode 100644 index 000000000..99f951a95 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_parallel/_run.html @@ -0,0 +1,769 @@ + + + + + + + + scitex_parallel._run — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_parallel._run

+#!/usr/bin/env python3
+# File: src/scitex_parallel/_run.py
+
+"""
+Parallel execution utilities using ThreadPoolExecutor.
+
+1. Functionality:
+   - Runs functions in parallel using ThreadPoolExecutor
+   - Handles both single and multiple return values
+   - Supports automatic CPU core detection
+2. Input:
+   - Function to run
+   - List of items to process
+   - Optional parameters for execution control
+3. Output:
+   - List of results or concatenated tuple
+4. Prerequisites:
+   - concurrent.futures
+   - tqdm
+"""
+
+import multiprocessing
+import warnings
+from concurrent.futures import ThreadPoolExecutor, as_completed
+from typing import Any, Callable, List
+
+from tqdm import tqdm
+
+
+
+[docs] +def run( + func: Callable, + args_list: List[tuple], + n_jobs: int = -1, + desc: str = "Processing", +) -> List[Any]: + """Runs function in parallel using ThreadPoolExecutor with tuple arguments. + + Parameters + ---------- + func : Callable + Function to run in parallel + args_list : List[tuple] + List of argument tuples, each tuple contains arguments for one function call + n_jobs : int, optional + Number of jobs to run in parallel. -1 means using all processors + desc : str, optional + Description for progress bar + + Returns + ------- + List[Any] + Results of parallel execution + + Examples + -------- + >>> def add(x, y): + ... return x + y + >>> args_list = [(1, 4), (2, 5), (3, 6)] + >>> run(add, args_list) + [5, 7, 9] + """ + if not args_list: + raise ValueError("Args list cannot be empty") + if not callable(func): + raise ValueError("Func must be callable") + + # Validate n_jobs before conversion: must be -1 or >= 1 + if n_jobs < -1 or n_jobs == 0: + raise ValueError("n_jobs must be >= 1 or -1") + + cpu_count = multiprocessing.cpu_count() + n_jobs = cpu_count if n_jobs == -1 else n_jobs + + if n_jobs > cpu_count: + warnings.warn(f"n_jobs ({n_jobs}) is greater than CPU count ({cpu_count})") + + results = [None] * len(args_list) # Pre-allocate list + + with ThreadPoolExecutor(max_workers=n_jobs) as executor: + futures = { + executor.submit(func, *args): idx for idx, args in enumerate(args_list) + } + for future in tqdm(as_completed(futures), total=len(args_list), desc=desc): + idx = futures[future] + results[idx] = future.result() + + # If results contain multiple values (tuples), transpose them + if results and isinstance(results[0], tuple): + n_vars = len(results[0]) + return tuple([result[i] for result in results] for i in range(n_vars)) + + return results
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_path/_clean.html b/src/scitex/_sphinx_html/_modules/scitex_path/_clean.html new file mode 100644 index 000000000..02a5d9f8a --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_path/_clean.html @@ -0,0 +1,732 @@ + + + + + + + + scitex_path._clean — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_path._clean

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Time-stamp: "2025-05-15 00:55:30 (ywatanabe)"
+# File: /data/gpfs/projects/punim2354/ywatanabe/scitex_repo/src/scitex/path/_clean.py
+
+import os
+
+
+
+[docs] +def clean(path_string): + """Cleans and normalizes a file system path string. + + Example + ------- + >>> clean('/home/user/./folder/../file.txt') + '/home/user/file.txt' + >>> clean('path/./to//file.txt') + 'path/to/file.txt' + >>> clean('path with spaces') + 'path_with_spaces' + + Parameters + ---------- + path_string : str + File path to clean + + Returns + ------- + str + Normalized path string + """ + # Convert Path objects to strings to avoid AttributeError + if hasattr(path_string, "__fspath__"): # Check if it's a path-like object + path_string = str(path_string) + + if not path_string: + return "" + + # Remember if path ends with a slash (indicating a directory) + is_directory = path_string.endswith("/") + + # Replace spaces with underscores + path_string = path_string.replace(" ", "_") + + # Use normpath to handle ../ and ./ references + cleaned_path = os.path.normpath(path_string) + + # Replace multiple slashes with single slash + while "//" in cleaned_path: + cleaned_path = cleaned_path.replace("//", "/") + + # Restore trailing slash if it was a directory + if is_directory and not cleaned_path.endswith("/"): + cleaned_path += "/" + + return cleaned_path
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_path/_find.html b/src/scitex/_sphinx_html/_modules/scitex_path/_find.html new file mode 100644 index 000000000..83915db6d --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_path/_find.html @@ -0,0 +1,776 @@ + + + + + + + + scitex_path._find — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_path._find

+#!/usr/bin/env python3
+# Timestamp: "2026-01-08 02:00:00 (ywatanabe)"
+# File: /home/ywatanabe/proj/scitex-code/src/scitex/path/_find.py
+
+"""File and directory finding utilities."""
+
+import fnmatch
+import os
+from pathlib import Path
+from typing import List, Optional, Union
+
+
+
+[docs] +def find_git_root() -> str: + """Find the root directory of the current git repository. + + Returns + ------- + str + Path to the git repository root. + """ + import git + + repo = git.Repo(".", search_parent_directories=True) + return repo.working_tree_dir
+ + + +
+[docs] +def find_dir(root_dir: Union[str, Path], exp: Union[str, List[str]]) -> List[str]: + """Find directories matching pattern.""" + return _find(root_dir, type="d", exp=exp)
+ + + +
+[docs] +def find_file(root_dir: Union[str, Path], exp: Union[str, List[str]]) -> List[str]: + """Find files matching pattern.""" + return _find(root_dir, type="f", exp=exp)
+ + + +def _find( + rootdir: Union[str, Path], + type: Optional[str] = "f", + exp: Union[str, List[str]] = "*", +) -> List[str]: + """Mimics the Unix find command. + + Parameters + ---------- + rootdir : str or Path + Root directory to search in. + type : str, optional + 'f' for files, 'd' for directories, None for both. + exp : str or list of str + Pattern(s) to match. + + Returns + ------- + list of str + Matching paths. + + Example + ------- + >>> _find('/path/to/search', "f", "*.txt") + """ + rootdir = str(rootdir) + if isinstance(exp, str): + exp = [exp] + + exclude_keys = ["/lib/", "/env/", "/build/"] + matches: List[str] = [] + + for dirpath, dirnames, filenames in os.walk(rootdir): + candidates = [] + if type in (None, "f"): + candidates.extend((os.path.join(dirpath, name), "f") for name in filenames) + if type in (None, "d"): + candidates.extend((os.path.join(dirpath, name), "d") for name in dirnames) + + for full_path, kind in candidates: + # Verify type (tests mock os.path.isfile / isdir) + if kind == "f" and not os.path.isfile(full_path): + continue + if kind == "d" and not os.path.isdir(full_path): + continue + + name = os.path.basename(full_path) + if not any(fnmatch.fnmatch(name, _exp) for _exp in exp if _exp): + continue + + if any(ek in full_path for ek in exclude_keys): + continue + + if full_path not in matches: + matches.append(full_path) + + return matches + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_path/_get_module_path.html b/src/scitex/_sphinx_html/_modules/scitex_path/_get_module_path.html new file mode 100644 index 000000000..74be66048 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_path/_get_module_path.html @@ -0,0 +1,730 @@ + + + + + + + + scitex_path._get_module_path — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_path._get_module_path

+#!/usr/bin/env python3
+# Timestamp: "2026-01-08 02:00:00 (ywatanabe)"
+# File: /home/ywatanabe/proj/scitex-code/src/scitex/path/_get_module_path.py
+
+"""Module path utilities."""
+
+import importlib.util
+from pathlib import Path
+
+
+
+[docs] +def get_data_path_from_a_package(package_str: str, resource: str) -> Path: + """Get the path to a data file within a package. + + Parameters + ---------- + package_str : str + The name of the package as a string. + resource : str + The name of the resource file within the package's data directory. + + Returns + ------- + Path + The full path to the resource file. + + Raises + ------ + ImportError + If the specified package cannot be found. + FileNotFoundError + If the resource file does not exist in the package's data directory. + """ + spec = importlib.util.find_spec(package_str) + if spec is None: + raise ImportError(f"Package '{package_str}' not found") + + origin = Path(spec.origin) + # Walk up until we reach the `src/` layout boundary, then pick the + # sibling `data/` directory at the project root. Falls back to the + # filesystem root if no `src/` parent exists. + data_dir = origin.parents[0] + while data_dir.name != "src" and data_dir != data_dir.parent: + data_dir = data_dir.parent + data_dir = data_dir.parent / "data" + + resource_path = data_dir / resource + + if not resource_path.exists(): + raise FileNotFoundError( + f"Resource '{resource}' not found in package '{package_str}'" + ) + + return resource_path
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_path/_getsize.html b/src/scitex/_sphinx_html/_modules/scitex_path/_getsize.html new file mode 100644 index 000000000..ff8503fd4 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_path/_getsize.html @@ -0,0 +1,711 @@ + + + + + + + + scitex_path._getsize — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_path._getsize

+#!/usr/bin/env python3
+# Timestamp: "2026-01-08 02:00:00 (ywatanabe)"
+# File: scitex-path/src/scitex_path/_getsize.py
+
+"""File size utilities."""
+
+import math
+import os
+from pathlib import Path
+from typing import Union
+
+
+
+[docs] +def getsize(path: Union[str, Path]) -> Union[int, float]: + """Get file size in bytes. + + Parameters + ---------- + path : str or Path + Path to file. + + Returns + ------- + int or float + File size in bytes, or math.nan if file doesn't exist. + + Raises + ------ + PermissionError + If the file cannot be accessed due to permissions. + """ + path_str = os.fspath(path) + if os.path.exists(path_str): + return os.path.getsize(path_str) + return math.nan
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_path/_mk_spath.html b/src/scitex/_sphinx_html/_modules/scitex_path/_mk_spath.html new file mode 100644 index 000000000..00ee7bac8 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_path/_mk_spath.html @@ -0,0 +1,721 @@ + + + + + + + + scitex_path._mk_spath — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_path._mk_spath

+#!/usr/bin/env python3
+# Timestamp: "2026-01-08 02:00:00 (ywatanabe)"
+# File: /home/ywatanabe/proj/scitex-code/src/scitex/path/_mk_spath.py
+
+"""Save path creation utilities."""
+
+import inspect
+import os
+from pathlib import Path
+from typing import Union
+
+from ._split import split
+
+
+
+[docs] +def mk_spath(sfname: Union[str, Path], makedirs: bool = False) -> str: + """Create a save path based on the calling script's location. + + Parameters + ---------- + sfname : str or Path + The name of the file to be saved. + makedirs : bool, optional + If True, create the directory structure for the save path. + + Returns + ------- + str + The full save path for the file. + + Example + ------- + >>> spath = mk_spath('output.txt', makedirs=True) + """ + caller_file = inspect.stack()[1].filename + if "ipython" in caller_file.lower(): + caller_file = f"/tmp/fake-{os.getenv('USER')}.py" + + sdir, sfname_mod, _ = split(__file__) + spath = sdir + sfname_mod + "/" + str(sfname) + + if makedirs: + os.makedirs(os.path.dirname(spath), exist_ok=True) + + return spath
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_path/_split.html b/src/scitex/_sphinx_html/_modules/scitex_path/_split.html new file mode 100644 index 000000000..03ec2ed38 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_path/_split.html @@ -0,0 +1,716 @@ + + + + + + + + scitex_path._split — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_path._split

+#!/usr/bin/env python3
+# Timestamp: "2026-01-08 02:00:00 (ywatanabe)"
+# File: /home/ywatanabe/proj/scitex-code/src/scitex/path/_split.py
+
+"""Path splitting utilities."""
+
+import os
+from pathlib import Path
+from typing import Tuple, Union
+
+
+
+[docs] +def split(fpath: Union[str, Path]) -> Tuple[str, str, str]: + """Split a file path into directory, filename, and extension. + + Parameters + ---------- + fpath : str or Path + File path to split. + + Returns + ------- + tuple of (str, str, str) + (directory with trailing slash, filename without extension, extension) + + Example + ------- + >>> dirname, fname, ext = split('/path/to/file.txt') + >>> dirname + '/path/to/' + >>> fname + 'file' + >>> ext + '.txt' + """ + fpath = os.fspath(fpath) if not isinstance(fpath, str) else fpath + dirname = os.path.dirname(fpath) + "/" + basename = os.path.basename(fpath) + fname, ext = os.path.splitext(basename) + return dirname, fname, ext
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_path/_symlink.html b/src/scitex/_sphinx_html/_modules/scitex_path/_symlink.html new file mode 100644 index 000000000..bb672581c --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_path/_symlink.html @@ -0,0 +1,990 @@ + + + + + + + + scitex_path._symlink — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_path._symlink

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Timestamp: "2025-09-16 15:11:33 (ywatanabe)"
+# File: scitex-path/src/scitex_path/_symlink.py
+# ----------------------------------------
+from __future__ import annotations
+
+import os
+
+__FILE__ = __file__
+__DIR__ = os.path.dirname(__FILE__)
+# ----------------------------------------
+
+"""Symlink creation and management utilities."""
+
+import logging
+from pathlib import Path
+from typing import Optional, Union
+
+logger = logging.getLogger(__name__)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# EOF
+
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_path/_this_path.html b/src/scitex/_sphinx_html/_modules/scitex_path/_this_path.html new file mode 100644 index 000000000..85aab4d2e --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_path/_this_path.html @@ -0,0 +1,703 @@ + + + + + + + + scitex_path._this_path — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_path._this_path

+#!/usr/bin/env python3
+# Timestamp: "2026-01-08 02:00:00 (ywatanabe)"
+# File: /home/ywatanabe/proj/scitex-code/src/scitex/path/_this_path.py
+
+"""Get current file path utilities."""
+
+import inspect
+
+
+
+[docs] +def this_path(ipython_fake_path: str = "/tmp/fake.py") -> str: + """Get the path of the calling script. + + Note + ---- + This function historically captures the caller's filename via + ``inspect.stack()[1]`` but then returns this module's ``__file__``. + The tests codify that legacy behavior; do not change without + updating callers. + """ + THIS_FILE = inspect.stack()[1].filename # noqa: F841 - kept for compatibility + if "ipython" in (THIS_FILE or "").lower(): + THIS_FILE = ipython_fake_path # noqa: F841 + return __file__
+ + + +get_this_path = this_path + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_path/_version.html b/src/scitex/_sphinx_html/_modules/scitex_path/_version.html new file mode 100644 index 000000000..03e52d21a --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_path/_version.html @@ -0,0 +1,785 @@ + + + + + + + + scitex_path._version — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_path._version

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Time-stamp: "2024-11-02 20:48:24 (ywatanabe)"
+# File: scitex-path/src/scitex_path/_version.py
+
+import os
+import re
+from glob import glob
+
+
+
+[docs] +def find_latest(dirname, fname, ext, version_prefix="_v"): + """Find the latest versioned file in a directory. + + Parameters + ---------- + dirname : str + Directory to search in. + fname : str + Base filename without version number or extension. + ext : str + File extension including the dot (e.g., '.txt'). + version_prefix : str, optional + Prefix before the version number. Default is '_v'. + + Returns + ------- + str or None + Path to the latest versioned file, or None if not found. + """ + version_pattern = re.compile( + rf"({re.escape(fname)}{re.escape(version_prefix)})(\d+)({re.escape(ext)})$" + ) + + glob_pattern = os.path.join(dirname, f"{fname}{version_prefix}*{ext}") + files = glob(glob_pattern) + + highest_version = 0 + latest_file = None + + for file in files: + filename = os.path.basename(file) + match = version_pattern.search(filename) + if match: + version_num = int(match.group(2)) + if version_num > highest_version: + highest_version = version_num + latest_file = file + + return latest_file
+ + + +
+[docs] +def increment_version(dirname, fname, ext, version_prefix="_v"): + """Generate the next version of a filename based on existing versioned files. + + Parameters + ---------- + dirname : str + Directory to search in. + fname : str + Base filename without version number or extension. + ext : str + File extension including the dot (e.g., '.txt'). + version_prefix : str, optional + Prefix before the version number. Default is '_v'. + + Returns + ------- + str + Full path for the next version of the file. + + Example + ------- + >>> increment_version('/path/to/dir', 'myfile', '.txt') + '/path/to/dir/myfile_v001.txt' + """ + version_pattern = re.compile( + rf"({re.escape(fname)}{re.escape(version_prefix)})(\d+)({re.escape(ext)})$" + ) + + glob_pattern = os.path.join(dirname, f"{fname}{version_prefix}*{ext}") + files = glob(glob_pattern) + + highest_version = 0 + base, suffix = None, None + + for file in files: + filename = os.path.basename(file) + match = version_pattern.search(filename) + if match: + base, version_str, suffix = match.groups() + version_num = int(version_str) + if version_num > highest_version: + highest_version = version_num + + if base is None or suffix is None: + base = f"{fname}{version_prefix}" + suffix = ext + highest_version = 0 + + next_version_number = highest_version + 1 + next_version_str = f"{base}{next_version_number:03d}{suffix}" + + next_filepath = os.path.join(dirname, next_version_str) + + return next_filepath
+ + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_repro.html b/src/scitex/_sphinx_html/_modules/scitex_repro.html new file mode 100644 index 000000000..f514262b7 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_repro.html @@ -0,0 +1,751 @@ + + + + + + + + scitex_repro — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_repro

+#!/usr/bin/env python3
+"""
+scitex-repro — Reproducibility utilities for scientific computing.
+
+Provides tools for reproducible scientific computing:
+- Random state management (RandomStateManager)
+- ID generation (gen_ID)
+- Timestamp generation (gen_timestamp)
+- Array hashing (hash_array)
+"""
+
+from __future__ import annotations
+
+# ID and timestamp utilities
+from ._gen_ID import gen_ID, gen_id
+from ._gen_timestamp import gen_timestamp, timestamp
+
+# Hash utilities
+from ._hash_array import hash_array
+
+# Random state management
+from ._RandomStateManager import RandomStateManager, get, reset
+
+
+# Legacy function for backward compatibility
+
+[docs] +def fix_seeds( + seed=42, + os=True, + random=True, + np=True, + torch=True, + tf=False, + jax=False, + verbose=False, + **kwargs, +): + """ + Deprecated: Use RandomStateManager instead. + + This function maintains backward compatibility with the old fix_seeds API. + """ + import warnings + + warnings.warn( + "fix_seeds is deprecated. Use scitex_repro.RandomStateManager instead.", + DeprecationWarning, + stacklevel=2, + ) + return RandomStateManager(seed=seed, verbose=verbose)
+ + + +__all__ = [ + "__version__", + # ID and timestamp utilities + "gen_ID", + "gen_id", + "gen_timestamp", + "timestamp", + # Hash utilities + "hash_array", + # Random state management + "RandomStateManager", + "get", + "reset", + # Legacy (deprecated) + "fix_seeds", +] + +try: + from importlib.metadata import version as _v, PackageNotFoundError + try: + __version__ = _v("scitex-repro") + except PackageNotFoundError: + __version__ = "0.0.0+local" + del _v, PackageNotFoundError +except ImportError: # pragma: no cover — only on ancient Pythons + __version__ = "0.0.0+local" +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_repro/_RandomStateManager.html b/src/scitex/_sphinx_html/_modules/scitex_repro/_RandomStateManager.html new file mode 100644 index 000000000..121d4fcd2 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_repro/_RandomStateManager.html @@ -0,0 +1,1316 @@ + + + + + + + + scitex_repro._RandomStateManager — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_repro._RandomStateManager

+#!/usr/bin/env python3
+# Extracted from scitex-python/src/scitex/repro/_RandomStateManager.py
+# ----------------------------------------
+from __future__ import annotations
+
+import os
+
+__FILE__ = __file__
+__DIR__ = os.path.dirname(__FILE__)
+# ----------------------------------------
+
+"""
+Clean, simple RandomStateManager for scientific reproducibility.
+
+Main API:
+    rng = RandomStateManager(seed=42)   # Create instance
+    gen = rng("name")                   # Get named generator
+    rng.verify(obj, "name")             # Verify reproducibility
+"""
+
+import hashlib
+import json
+import logging
+import pickle
+from contextlib import contextmanager
+from pathlib import Path
+from typing import Any
+
+from scitex_repro._config import get_paths
+
+logger = logging.getLogger(__name__)
+
+# Global singleton instance
+_GLOBAL_INSTANCE = None
+
+
+
+[docs] +class RandomStateManager: + """ + Simple, robust random state manager for scientific computing. + + Examples + -------- + >>> from scitex_repro import RandomStateManager + >>> + >>> # Method 1: Direct usage + >>> rng = RandomStateManager(seed=42) + >>> data = rng("data").random(100) + >>> + >>> # Verify reproducibility + >>> rng.verify(data, "my_data") + """ + +
+[docs] + def __init__(self, seed: int = 42, verbose=False): + """Initialize with automatic module detection.""" + self.seed = seed + self.verbose = verbose + self._generators = {} + self._cache_dir = get_paths().rng + self._cache_dir.mkdir(parents=True, exist_ok=True) + self._jax_key = None # Initialize to None, will be set if jax is available + + if verbose: + logger.info(f"RandomStateManager initialized with seed {seed}") + + # Auto-fix all available seeds + self._auto_fix_seeds(verbose=verbose)
+ + + def _auto_fix_seeds(self, verbose=None): + """Automatically detect and fix ALL available random modules.""" + # Use instance verbose if not specified + if verbose is None: + verbose = self.verbose + + # OS environment + os.environ["PYTHONHASHSEED"] = str(self.seed) + os.environ["CUBLAS_WORKSPACE_CONFIG"] = ":4096:8" + + fixed_modules = [] + + # Python random + try: + import random + + random.seed(self.seed) + fixed_modules.append("random") + except ImportError: + pass + + # NumPy + try: + import numpy as np + + np.random.seed(self.seed) + # Also set default_rng for new API + self._np = np + self._np_default_rng = np.random.default_rng(self.seed) + fixed_modules.append("numpy") + except ImportError: + self._np = None + + # PyTorch + try: + import torch + + torch.manual_seed(self.seed) + if torch.cuda.is_available(): + torch.cuda.manual_seed_all(self.seed) + torch.backends.cudnn.deterministic = True + torch.backends.cudnn.benchmark = False + fixed_modules.append("torch+cuda") + else: + fixed_modules.append("torch") + except Exception as e: + # ImportError: torch not installed (silent skip). + # Anything else (e.g. CUDA driver mismatch, broken install): log + # at debug and skip. Auto-seed is best-effort — a misconfigured + # ML runtime should never block @stx.session for non-ML users. + logger.debug(f"torch seed skipped: {type(e).__name__}: {e}") + + # TensorFlow + try: + import tensorflow as tf + + tf.random.set_seed(self.seed) + fixed_modules.append("tensorflow") + except Exception as e: + # ImportError: tf not installed. + # google.protobuf.runtime_version.VersionError: gencode/runtime + # protobuf mismatch — surfaced once, then swallowed so the + # rest of session.start can proceed. + logger.debug(f"tensorflow seed skipped: {type(e).__name__}: {e}") + + # JAX (deferred import to avoid circular imports) + try: + import jax + + self._jax_key = jax.random.PRNGKey(self.seed) + fixed_modules.append("jax") + except (ImportError, AttributeError, RuntimeError): + # ImportError: jax not installed + # AttributeError: circular import in jax._src.clusters + # RuntimeError: other jax initialization errors + self._jax_key = None + pass + + # Importing TensorFlow / PyTorch during the framework-seeding pass + # can consume numpy global entropy on first init (lazy variable + # creation, autotune probes). The first RandomStateManager(seed=N) + # then leaves numpy at state(seed=N + K_init); a second call leaves + # it at state(seed=N) because TF/torch are already imported and + # K_init = 0. That asymmetry breaks `np.allclose(a, b)` across two + # construction sites in user code (see examples/quickstart.py). + # Re-seeding numpy as the last step normalises the post-init state + # to seed=N regardless of whether frameworks were just imported. + try: + import numpy as _np + + _np.random.seed(self.seed) + except ImportError: + pass + + if verbose and fixed_modules: + logger.info(f"Fixed random seeds for: {', '.join(fixed_modules)}") + +
+[docs] + def get_np_generator(self, name: str): + """ + Get or create a named NumPy random generator. + + Parameters + ---------- + name : str + Generator name (e.g., "data", "model", "augment") + + Returns + ------- + numpy.random.Generator + Independent NumPy random generator + + Examples + -------- + >>> rng = RandomStateManager(42) + >>> gen = rng.get_np_generator("data") + >>> values = gen.random(100) + >>> perm = gen.permutation(100) + """ + if self._np is None: + raise ImportError("NumPy required for random generators") + + if name not in self._generators: + # Create deterministic seed from name + name_hash = int(hashlib.md5(name.encode()).hexdigest()[:8], 16) + seed = (self.seed + name_hash) % (2**32) + self._generators[name] = self._np.random.default_rng(seed) + + return self._generators[name]
+ + +
+[docs] + def __call__(self, name: str, verbose: bool = None): + """ + Get or create a named NumPy random generator. + + This is a backward compatibility wrapper for get_np_generator(). + Consider using get_np_generator() directly for clarity. + + Parameters + ---------- + name : str + Generator name + verbose : bool, optional + Whether to show deprecation warning + + Returns + ------- + numpy.random.Generator + NumPy random generator with deterministic seed + """ + if verbose: + print( + f"Note: rng('{name}') is deprecated. Use rng.get_np_generator('{name}') instead." + ) + return self.get_np_generator(name)
+ + +
+[docs] + def verify(self, obj: Any, name: str = None, verbose: bool = True) -> bool: + """ + Verify object matches cached hash (detects broken reproducibility). + + First call: caches the object's hash + Later calls: verifies object matches cached hash + + Parameters + ---------- + obj : Any + Object to verify (array, tensor, data, model weights, etc.) + Supports: numpy arrays, torch tensors, tf tensors, jax arrays, + lists, dicts, pandas dataframes, and basic types + name : str, optional + Cache name. Auto-generated if not provided. + + Returns + ------- + bool + True if matches cache (or first call), False if different + + Examples + -------- + >>> data = generate_data() + >>> rng.verify(data, "train_data") # First run: caches + >>> # Next run: + >>> rng.verify(data, "train_data") # Verifies match + """ + + # Auto-generate name if needed + if name is None: + import inspect + + frame = inspect.currentframe().f_back + filename = Path(frame.f_code.co_filename).stem + lineno = frame.f_lineno + name = f"{filename}_L{lineno}" + + # Sanitize name + safe_name = "".join(c if c.isalnum() or c in "-_" else "_" for c in name) + cache_file = self._cache_dir / f"{safe_name}.json" + + # Compute hash based on object type + obj_hash = self._compute_hash(obj) + + # Use instance verbose if not specified + if verbose is None: + verbose = self.verbose + + # Check cache + if cache_file.exists(): + with open(cache_file) as f: + cached = json.load(f) + + matches = cached["hash"] == obj_hash + if not matches and verbose: + print(f"WARNING: Reproducibility broken for '{name}'!") + print(f" Expected: {cached['hash'][:16]}...") + print(f" Got: {obj_hash[:16]}...") + raise ValueError(f"Reproducibility verification failed for '{name}'") + elif matches and verbose: + print(f"OK: Reproducibility verified for '{name}'") + + return matches + else: + # First call - cache it + with open(cache_file, "w") as f: + json.dump({"name": name, "hash": obj_hash, "seed": self.seed}, f) + return True
+ + + def _compute_hash(self, obj: Any) -> str: + """ + Compute hash for various object types. + + Supports: + - NumPy arrays + - PyTorch tensors + - TensorFlow tensors + - JAX arrays + - Pandas DataFrames/Series + - Lists, tuples, dicts + - Basic types (int, float, str, bool) + """ + import numpy as np + + # NumPy array + if isinstance(obj, np.ndarray): + return hashlib.sha256(obj.tobytes()).hexdigest()[:32] + + # PyTorch tensor + try: + import torch + + if isinstance(obj, torch.Tensor): + # Move to CPU and convert to numpy for consistent hashing + obj_np = obj.detach().cpu().numpy() + return hashlib.sha256(obj_np.tobytes()).hexdigest()[:32] + except ImportError: + pass + + # TensorFlow tensor — catch any import-time failure (protobuf + # runtime version mismatch raises VersionError, not ImportError). + try: + import tensorflow as tf + + if isinstance(obj, (tf.Tensor, tf.Variable)): + obj_np = obj.numpy() + return hashlib.sha256(obj_np.tobytes()).hexdigest()[:32] + except Exception: + pass + + # JAX array + try: + import jax.numpy as jnp + + if isinstance(obj, jnp.ndarray): + obj_np = np.array(obj) + return hashlib.sha256(obj_np.tobytes()).hexdigest()[:32] + except (ImportError, AttributeError, RuntimeError): + pass + + # Pandas DataFrame/Series + try: + import pandas as pd + + if isinstance(obj, (pd.DataFrame, pd.Series)): + obj_str = obj.to_json(orient="split", date_format="iso") + return hashlib.sha256(obj_str.encode()).hexdigest()[:32] + except ImportError: + pass + + # Lists and tuples - convert to numpy array if numeric + if isinstance(obj, (list, tuple)): + try: + obj_np = np.array(obj) + if obj_np.dtype != object: # Numeric array + return hashlib.sha256(obj_np.tobytes()).hexdigest()[:32] + except: + pass + # Fall through to string representation + + # Dictionaries - serialize to JSON + if isinstance(obj, dict): + try: + obj_str = json.dumps(obj, sort_keys=True, default=str) + return hashlib.sha256(obj_str.encode()).hexdigest()[:32] + except: + pass + + # Default: convert to string + obj_str = str(obj) + return hashlib.sha256(obj_str.encode()).hexdigest()[:32] + +
+[docs] + def checkpoint(self, name: str = "checkpoint"): + """Save current state of all generators.""" + checkpoint_file = self._cache_dir / f"{name}.pkl" + state = { + "seed": self.seed, + "generators": { + k: v.bit_generator.state for k, v in self._generators.items() + }, + } + with open(checkpoint_file, "wb") as f: + pickle.dump(state, f) + return checkpoint_file
+ + +
+[docs] + def restore(self, checkpoint): + """Restore from checkpoint.""" + if isinstance(checkpoint, str): + checkpoint = Path(checkpoint) + + with open(checkpoint, "rb") as f: + state = pickle.load(f) + + self.seed = state["seed"] + self._auto_fix_seeds() + + # Restore generator states + for name, gen_state in state["generators"].items(): + gen = self(name) + gen.bit_generator.state = gen_state
+ + +
+[docs] + @contextmanager + def temporary_seed(self, seed: int): + """Context manager for temporary seed change.""" + import random + + import numpy as np + + # Save current states + old_random_state = random.getstate() + old_np_state = np.random.get_state() if self._np else None + + # Set temporary seed + random.seed(seed) + if self._np: + np.random.seed(seed) + + try: + yield + finally: + # Restore states + random.setstate(old_random_state) + if self._np and old_np_state: + np.random.set_state(old_np_state)
+ + +
+[docs] + def get_sklearn_random_state(self, name: str): + """ + Get a random state for scikit-learn. + + Scikit-learn uses integers for random_state parameter. + + Parameters + ---------- + name : str + Generator name + + Returns + ------- + int + Random state integer for sklearn + + Examples + -------- + >>> rng = RandomStateManager(42) + >>> from sklearn.model_selection import train_test_split + >>> X_train, X_test = train_test_split( + ... X, test_size=0.2, + ... random_state=rng.get_sklearn_random_state("split") + ... ) + """ + # Create deterministic seed from name + name_hash = int(hashlib.md5(name.encode()).hexdigest()[:8], 16) + seed = (self.seed + name_hash) % (2**32) + return seed
+ + +
+[docs] + def get_torch_generator(self, name: str): + """ + Get or create a named PyTorch generator. + + Parameters + ---------- + name : str + Generator name + + Returns + ------- + torch.Generator + PyTorch generator with deterministic seed + + Examples + -------- + >>> rng = RandomStateManager(42) + >>> gen = rng.get_torch_generator("model") + >>> torch.randn(5, 5, generator=gen) + """ + try: + import torch + except Exception as e: + # ImportError: torch not installed. + # Anything else (e.g. CUDA driver mismatch, broken install) + # surfaces with the original message so the user can diagnose. + raise ImportError(f"PyTorch unavailable: {type(e).__name__}: {e}") + + if not hasattr(self, "_torch_generators"): + self._torch_generators = {} + + if name not in self._torch_generators: + # Create deterministic seed from name + name_hash = int(hashlib.md5(name.encode()).hexdigest()[:8], 16) + seed = (self.seed + name_hash) % (2**32) + + gen = torch.Generator() + gen.manual_seed(seed) + self._torch_generators[name] = gen + + return self._torch_generators[name]
+ + +
+[docs] + def get_generator(self, name: str): + """Alias for get_np_generator for compatibility.""" + return self.get_np_generator(name)
+ + +
+[docs] + def clear_cache(self, patterns: str | list[str] = None) -> int: + """ + Clear verification cache files. + + Parameters + ---------- + patterns : str or list of str, optional + Specific cache patterns to clear. If None, clears all. + + Returns + ------- + int + Number of cache files removed + """ + + if not self._cache_dir.exists(): + return 0 + + removed_count = 0 + + if patterns is None: + # Clear all .json files + cache_files = list(self._cache_dir.glob("*.json")) + for cache_file in cache_files: + cache_file.unlink() + removed_count += 1 + else: + # Ensure patterns is a list + if isinstance(patterns, str): + patterns = [patterns] + + for pattern in patterns: + # Handle glob patterns + if "*" in pattern or "?" in pattern: + cache_files = list(self._cache_dir.glob(f"{pattern}.json")) + else: + # Exact match + cache_file = self._cache_dir / f"{pattern}.json" + cache_files = [cache_file] if cache_file.exists() else [] + + for cache_file in cache_files: + cache_file.unlink() + removed_count += 1 + + return removed_count
+
+ + + +
+[docs] +def get(verbose: bool = False) -> RandomStateManager: + """ + Get or create the global RandomStateManager instance. + + Parameters + ---------- + verbose : bool, optional + Whether to print status messages (default: False) + + Returns + ------- + RandomStateManager + Global instance + + Examples + -------- + >>> from scitex_repro import get + >>> rng = get() + >>> data = rng("data").random(100) + """ + global _GLOBAL_INSTANCE + + if _GLOBAL_INSTANCE is None: + _GLOBAL_INSTANCE = RandomStateManager(42, verbose=verbose) + + return _GLOBAL_INSTANCE
+ + + +
+[docs] +def reset(seed: int = 42, verbose: bool = False) -> RandomStateManager: + """ + Reset global RandomStateManager with new seed. + + Parameters + ---------- + seed : int + New seed value + verbose : bool, optional + Whether to print status messages (default: False) + + Returns + ------- + RandomStateManager + New global instance + + Examples + -------- + >>> from scitex_repro import reset + >>> rng = reset(seed=123) + """ + global _GLOBAL_INSTANCE + _GLOBAL_INSTANCE = RandomStateManager(seed, verbose=verbose) + return _GLOBAL_INSTANCE
+ +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_repro/_gen_ID.html b/src/scitex/_sphinx_html/_modules/scitex_repro/_gen_ID.html new file mode 100644 index 000000000..4f7e78d2d --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_repro/_gen_ID.html @@ -0,0 +1,735 @@ + + + + + + + + scitex_repro._gen_ID — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_repro._gen_ID

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Time-stamp: "2024-11-07 17:53:38 (ywatanabe)"
+# Extracted from scitex-python/src/scitex/repro/_gen_ID.py
+
+import random as _random
+import string as _string
+from datetime import datetime as _datetime
+
+
+
+[docs] +def gen_id(time_format="%YY-%mM-%dD-%Hh%Mm%Ss", N=8, *, now_fn=None): + """Generate a unique identifier with timestamp and random characters. + + Creates a unique ID by combining a formatted timestamp with random + alphanumeric characters. Useful for creating unique experiment IDs, + run identifiers, or temporary file names. + + Parameters + ---------- + time_format : str, optional + Format string for timestamp portion. Default is "%YY-%mM-%dD-%Hh%Mm%Ss" + which produces "2025Y-05M-31D-12h30m45s" format. + N : int, optional + Number of random characters to append. Default is 8. + now_fn : callable, optional + Zero-argument callable returning a `datetime`-like object with + `.strftime()`. Defaults to `datetime.now`. Injection point for + deterministic tests — pass a fake that returns a fixed datetime + instead of mocking `datetime` globally. + + Returns + ------- + str + Unique identifier in format "{timestamp}_{random_chars}" + + Examples + -------- + >>> id1 = gen_id() + >>> print(id1) + '2025Y-05M-31D-12h30m45s_a3Bc9xY2' + + >>> id2 = gen_id(time_format="%Y%m%d", N=4) + >>> print(id2) + '20250531_xY9a' + + >>> # For experiment tracking + >>> exp_id = gen_id() + >>> save_path = f"results/experiment_{exp_id}.pkl" + """ + if now_fn is None: + now_fn = _datetime.now + now_str = now_fn().strftime(time_format) + rand_str = "".join( + [_random.choice(_string.ascii_letters + _string.digits) for i in range(N)] + ) + return now_str + "_" + rand_str
+ + + +# Backward compatibility +gen_ID = gen_id # Deprecated: use gen_id instead +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_repro/_gen_timestamp.html b/src/scitex/_sphinx_html/_modules/scitex_repro/_gen_timestamp.html new file mode 100644 index 000000000..75ed0468f --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_repro/_gen_timestamp.html @@ -0,0 +1,718 @@ + + + + + + + + scitex_repro._gen_timestamp — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_repro._gen_timestamp

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Time-stamp: "2024-11-07 17:44:32 (ywatanabe)"
+# Extracted from scitex-python/src/scitex/repro/_gen_timestamp.py
+
+from datetime import datetime as _datetime
+
+
+
+[docs] +def gen_timestamp(*, now_fn=None): + """Generate a timestamp string for file naming. + + Returns a timestamp in the format YYYY-MMDD-HHMM, suitable for + creating unique filenames or version identifiers. + + Parameters + ---------- + now_fn : callable, optional + Zero-argument callable returning a `datetime`-like object with + `.strftime()`. Defaults to `datetime.now`. Injection point for + deterministic tests — pass a fake that returns a fixed datetime + instead of mocking `datetime` globally. + + Returns + ------- + str + Timestamp string in format "YYYY-MMDD-HHMM" + + Examples + -------- + >>> timestamp = gen_timestamp() + >>> print(timestamp) + '2025-0531-1230' + + >>> filename = f"experiment_{gen_timestamp()}.csv" + >>> print(filename) + 'experiment_2025-0531-1230.csv' + """ + if now_fn is None: + now_fn = _datetime.now + return now_fn().strftime("%Y-%m%d-%H%M")
+ + + +timestamp = gen_timestamp +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_repro/_hash_array.html b/src/scitex/_sphinx_html/_modules/scitex_repro/_hash_array.html new file mode 100644 index 000000000..fc98a8ff2 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_repro/_hash_array.html @@ -0,0 +1,712 @@ + + + + + + + + scitex_repro._hash_array — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_repro._hash_array

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Extracted from scitex-python/src/scitex/repro/_hash_array.py
+from __future__ import annotations
+
+import hashlib
+
+import numpy as np
+
+
+
+[docs] +def hash_array(array_data: np.ndarray) -> str: + """Generate hash for array data. + + Creates a deterministic hash for numpy arrays, useful for + verifying data integrity and reproducibility. + + Parameters + ---------- + array_data : np.ndarray + Array to hash + + Returns + ------- + str + 16-character hash string + + Examples + -------- + >>> import numpy as np + >>> data = np.array([1, 2, 3, 4, 5]) + >>> hash1 = hash_array(data) + >>> hash2 = hash_array(data) + >>> hash1 == hash2 + True + """ + data_bytes = array_data.tobytes() + return hashlib.sha256(data_bytes).hexdigest()[:16]
+ +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_resource/_host.html b/src/scitex/_sphinx_html/_modules/scitex_resource/_host.html new file mode 100644 index 000000000..4b9b7ba7c --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_resource/_host.html @@ -0,0 +1,832 @@ + + + + + + + + scitex_resource._host — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_resource._host

+"""Host identity — canonical name and per-host config.
+
+scitex-resource owns the question "what host am I running on?". Other
+scitex-* packages (scitex-orochi, scitex-hpc, scitex-agent-container)
+consume :func:`get_host_name` so every package agrees on the same
+canonical name regardless of how the OS reports it (FQDN drift,
+``Yusukes-MacBook-Air`` vs ``mba``, login-node vs compute-node, etc.).
+
+Resolution cascade (highest precedence first):
+
+    1. ``$SCITEX_RESOURCE_HOST`` env var
+       (legacy ``$SCITEX_RESOURCE_MACHINE`` is still honoured as a
+       fallback to keep older shells working)
+    2. ``<project>/.scitex/resource/config.yaml`` ``host.canonical_name``
+       (falls back to legacy ``machine.canonical_name``)
+    3. ``~/.scitex/resource/config.yaml`` ``host.canonical_name``
+       (falls back to legacy ``machine.canonical_name``)
+    4. Short hostname (``socket.gethostname().split(".", 1)[0]``)
+
+The same chain applies to :func:`get_host_config` which returns the
+full ``host:`` block including ``aliases``, ``role``, ``hpc.*``.
+
+Config schema (``~/.scitex/resource/config.yaml`` — example for a SLURM
+login node):
+
+    host:
+      canonical_name: spartan
+      aliases:
+        - spartan-login1.hpc.example.edu
+        - spartan-login1
+      role: hpc-login
+      hpc:
+        cluster: spartan
+        login_only: true        # don't surface login-node CPU as available
+        partitions: [physical, sapphire]
+
+This file is part of the scitex local-state convention — see the
+``arch-local-state-directories`` skill in scitex-python for the full
+``.scitex/<pkg-short>/`` layout (config tracked, ``runtime/`` ignored).
+"""
+
+from __future__ import annotations
+
+import os
+import socket
+import warnings
+from pathlib import Path
+from typing import Any
+
+_PKG_SHORT = "resource"
+_ENV_VAR = "SCITEX_RESOURCE_HOST"
+_ENV_VAR_LEGACY = "SCITEX_RESOURCE_MACHINE"
+
+
+def _config_paths() -> list[Path]:
+    """Search order for ``config.yaml`` — project scope first, user fallback.
+
+    The project search walks from cwd up to (but stops AT) ``$HOME`` so
+    the user's ``~/.scitex/<pkg>/config.yaml`` is not mistaken for a
+    project hit when cwd happens to live under home (which it almost
+    always does on a workstation).
+    """
+    paths: list[Path] = []
+    cwd_root = Path.cwd()
+    home = Path.home().resolve()
+    for parent in (cwd_root, *cwd_root.parents):
+        try:
+            resolved = parent.resolve()
+        except OSError:
+            resolved = parent
+        if resolved == home:
+            break
+        candidate = parent / ".scitex" / _PKG_SHORT / "config.yaml"
+        if candidate.is_file():
+            paths.append(candidate)
+            break
+    user_root = Path(os.environ.get("SCITEX_DIR") or (Path.home() / ".scitex"))
+    user = user_root / _PKG_SHORT / "config.yaml"
+    if user.is_file():
+        paths.append(user)
+    return paths
+
+
+def _load_yaml(path: Path) -> dict[str, Any]:
+    try:
+        import yaml
+    except ImportError:
+        return {}
+    try:
+        with open(path) as f:
+            return yaml.safe_load(f) or {}
+    except (OSError, yaml.YAMLError):
+        return {}
+
+
+
+[docs] +def load_config() -> dict[str, Any]: + """Return the merged config dict — project file overrides user file. + + Empty dict if no config files exist or PyYAML isn't installed. + """ + merged: dict[str, Any] = {} + for path in reversed(_config_paths()): + data = _load_yaml(path) + merged.update(data) + return merged
+ + + +
+[docs] +def get_host_config() -> dict[str, Any]: + """Return the ``host:`` block from config (falls back to ``machine:``). + + If a config file declares only the legacy ``machine:`` key, a + one-time DeprecationWarning is emitted and that block is returned. + """ + cfg = load_config() + host_block = cfg.get("host") + if host_block: + return host_block + machine_block = cfg.get("machine") + if machine_block: + warnings.warn( + "config.yaml `machine:` block is deprecated; rename it to `host:`.", + DeprecationWarning, + stacklevel=2, + ) + return machine_block + return {}
+ + + +
+[docs] +def get_host_name() -> str: + """Return the canonical host name. + + See module docstring for the resolution cascade. Always returns a + non-empty string — the short hostname is the last-resort fallback. + """ + env = os.environ.get(_ENV_VAR, "").strip() + if env: + return env + legacy_env = os.environ.get(_ENV_VAR_LEGACY, "").strip() + if legacy_env: + return legacy_env + cfg = get_host_config() + canonical = (cfg.get("canonical_name") or "").strip() + if canonical: + return canonical + return _short_hostname()
+ + + +def _short_hostname() -> str: + try: + return socket.gethostname().split(".", 1)[0] or "unknown" + except OSError: + return "unknown" +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_resource/_log_processor_usages.html b/src/scitex/_sphinx_html/_modules/scitex_resource/_log_processor_usages.html new file mode 100644 index 000000000..a58d6fbe8 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_resource/_log_processor_usages.html @@ -0,0 +1,906 @@ + + + + + + + + scitex_resource._log_processor_usages — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for scitex_resource._log_processor_usages

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Time-stamp: "2024-11-04 16:28:53 (ywatanabe)"
+# File: ./scitex_repo/src/scitex/resource/_log_processor_usages.py
+
+"""
+Functionality:
+    * Monitors and logs system resource utilization over time
+Input:
+    * Path for saving logs
+    * Monitoring duration and interval
+Output:
+    * CSV file containing time-series resource usage data
+Prerequisites:
+    * scitex package with processor usage monitoring capabilities
+"""
+
+"""Imports"""
+import math
+import os
+import time
+from multiprocessing import Process
+from typing import Union
+
+import pandas as pd
+
+from ._compat import printc
+from ._specs import get_processor_usages
+
+
+# Vendored minimal load/save: we only need CSV here. Falls back to scitex.io
+# at call time for any non-CSV path so downstream behavior is unchanged when
+# the umbrella package is available.
+def load(path):
+    if str(path).endswith(".csv"):
+        return pd.read_csv(path)
+    try:  # pragma: no cover
+        from scitex_io._load import load as _load
+
+        return _load(path)
+    except ImportError:  # pragma: no cover
+        raise ImportError(
+            "Loading non-CSV resource logs requires scitex (umbrella). "
+            "Install with: pip install scitex"
+        )
+
+
+def save(obj, path, **kwargs):
+    import os
+
+    os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
+    if str(path).endswith(".csv") and isinstance(obj, pd.DataFrame):
+        obj.to_csv(path, **kwargs)
+        return
+    try:  # pragma: no cover
+        from scitex_io._save import save as _save
+
+        _save(obj, path, **kwargs)
+    except ImportError:  # pragma: no cover
+        raise ImportError(
+            "Saving non-CSV resource logs requires scitex (umbrella). "
+            "Install with: pip install scitex"
+        )
+
+
+# Defer scitex.sh import (scitex_sh is the standalone preferred path).
+def sh(cmd, **kwargs):
+    try:
+        from scitex_sh import sh as _sh
+
+        return _sh(cmd, **kwargs)
+    except ImportError:
+        import subprocess
+
+        if isinstance(cmd, str):
+            raise ValueError("scitex_resource: shell commands must be list-form")
+        return subprocess.run(cmd, capture_output=True, text=True, check=False).stdout
+
+
+"""Functions & Classes"""
+
+
+
+[docs] +def log_processor_usages( + path: str = "/tmp/scitex/processor_usages.csv", + limit_min: float = 30, + interval_s: float = 1, + init: bool = True, + verbose: bool = False, + background: bool = False, +) -> Union[None, Process]: + """Logs system resource usage over time. + + Parameters + ---------- + path : str + Path to save the log file + limit_min : float + Monitoring duration in minutes + interval_s : float + Sampling interval in seconds + init : bool + Whether to clear existing log file + verbose : bool + Whether to print the log + background : bool + Whether to run in background + + Returns + ------- + Union[None, Process] + Process object if background=True, None otherwise + """ + if background: + process = Process( + target=_log_processor_usages, + args=(path, limit_min, interval_s, init, verbose), + ) + process.start() + return process + + return _log_processor_usages( + path=path, + limit_min=limit_min, + interval_s=interval_s, + init=init, + verbose=verbose, + )
+ + + +def _log_processor_usages( + path: str = "/tmp/scitex/processor_usages.csv", + limit_min: float = 30, + interval_s: float = 1, + init: bool = True, + verbose: bool = False, +) -> None: + """Logs system resource usage over time. + + Parameters + ---------- + path : str + Path to save the log file + limit_min : float + Monitoring duration in minutes + interval_s : float + Sampling interval in seconds + init : bool + Whether to clear existing log file + verbose : bool + Whether to print the log + + Example + ------- + >>> log_processor_usages(path="usage_log.csv", limit_min=5) + """ + assert path.endswith(".csv"), "Path must end with .csv" + + # Log file initialization + _ensure_log_file(path, init) + printc(f"Log file can be monitored with with `tail -f {path}`") + + limit_s = limit_min * 60 + n_max = math.ceil(limit_s // interval_s) + + for _ in range(n_max): + _add(path, verbose=verbose) + time.sleep(interval_s) + + +# def _ensure_log_file(path: str, init: bool) -> None: +# def _create_path(path): +# os.makedirs(os.path.dirname(path), exist_ok=True) +# empty_df = pd.DataFrame() +# save(empty_df, path, verbose=False) +# printc(f"{path} created.") + +# if not os.path.exists(path): +# _create_path(path) + +# else: +# if init and os.path.exists(path): +# try: +# sh(f"rm -f {path}") +# _create_path(path) +# except Exception as err: +# raise RuntimeError(f"Failed to init log file: {err}") + +# def _add(path: str, verbose: bool = True) -> None: +# past = load(path) +# now = get_processor_usages() + +# combined = pd.concat([past, now]).round(3) +# save(combined, path, verbose=verbose) + + +def _add(path: str, verbose: bool = True) -> None: + """Appends current resource usage to CSV file.""" + now = get_processor_usages() + + # Append mode without loading entire file + with open(path, "a") as f: + now.to_csv(f, header=f.tell() == 0, index=False) + + +def _ensure_log_file(path: str, init: bool) -> None: + """Creates or reinitializes log file with headers.""" + + def _create_path(path): + os.makedirs(os.path.dirname(path), exist_ok=True) + # Write only headers + headers = ["Timestamp", "CPU [%]", "RAM [GiB]", "GPU [%]", "VRAM [GiB]"] + pd.DataFrame(columns=headers).to_csv(path, index=False) + printc(f"{path} created.") + + if not os.path.exists(path): + _create_path(path) + elif init: + try: + os.remove(path) + _create_path(path) + except Exception as err: + raise RuntimeError(f"Failed to init log file: {err}") + + +main = log_processor_usages + +if __name__ == "__main__": + main() + +# python -c "import scitex; scitex.resource.log_processor_usages(\"/tmp/processor_usages.csv\", init=True)" + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_resource/_machine.html b/src/scitex/_sphinx_html/_modules/scitex_resource/_machine.html new file mode 100644 index 000000000..c887dc2e2 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_resource/_machine.html @@ -0,0 +1,747 @@ + + + + + + + + scitex_resource._machine — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_resource._machine

+"""Deprecated alias module — use :mod:`scitex_resource._host` instead.
+
+This module is kept for back-compat. The canonical API moved to
+``scitex_resource._host``:
+
+* :func:`get_machine_name` → :func:`scitex_resource.get_host_name`
+* :func:`get_machine_config` → :func:`scitex_resource.get_host_config`
+
+Importing this module still works (no warning at import time so cold
+import paths stay quiet); the deprecation fires only when the legacy
+function names are actually called.
+"""
+
+from __future__ import annotations
+
+import warnings
+from typing import Any
+
+# Re-export the underlying helpers + names used by tests / CLI internals.
+from ._host import (  # noqa: F401
+    _ENV_VAR_LEGACY as _ENV_VAR,
+)
+from ._host import (
+    _PKG_SHORT,
+    _config_paths,
+    _load_yaml,
+    _short_hostname,
+    get_host_config,
+    get_host_name,
+    load_config,
+)
+
+_DEPRECATION_EMITTED: set[str] = set()
+
+
+def _warn_once(name: str, replacement: str) -> None:
+    if name in _DEPRECATION_EMITTED:
+        return
+    _DEPRECATION_EMITTED.add(name)
+    warnings.warn(
+        f"scitex_resource.{name} is deprecated; use scitex_resource.{replacement} instead.",
+        DeprecationWarning,
+        stacklevel=3,
+    )
+
+
+
+[docs] +def get_machine_config() -> dict[str, Any]: + """Deprecated alias for :func:`get_host_config`.""" + _warn_once("get_machine_config", "get_host_config") + return get_host_config()
+ + + +
+[docs] +def get_machine_name() -> str: + """Deprecated alias for :func:`get_host_name`.""" + _warn_once("get_machine_name", "get_host_name") + return get_host_name()
+ + + +__all__ = [ + "get_machine_config", + "get_machine_name", + "get_host_config", + "get_host_name", + "load_config", + "_PKG_SHORT", + "_ENV_VAR", + "_config_paths", + "_load_yaml", + "_short_hostname", +] +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_resource/_specs/_metrics.html b/src/scitex/_sphinx_html/_modules/scitex_resource/_specs/_metrics.html new file mode 100644 index 000000000..3933dd59c --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_resource/_specs/_metrics.html @@ -0,0 +1,829 @@ + + + + + + + + scitex_resource._specs._metrics — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for scitex_resource._specs._metrics

+"""Flat, machine-readable resource metrics for monitoring/heartbeat use.
+
+Companion to ``get_specs()`` (which is rich + human-formatted). ``get_metrics()``
+returns a flat dict of integers, floats and short strings — the shape any hub,
+dashboard, or heartbeat producer can ship over the wire without reshaping.
+
+Cross-platform via ``psutil`` (Linux / macOS / Windows / WSL). Container-aware:
+inside Docker / cgroups, ``psutil.virtual_memory()`` reports the cgroup limit,
+not the host kernel's view, so the numbers reflect what the process can
+actually use.
+
+Schema (treat as a public contract; bump minor on rename):
+
+    cpu_count          int     logical CPU count (psutil.cpu_count())
+    cpu_model          str     human-readable CPU model name; "" if unknown
+    load_avg_1m/5m/15m float   POSIX load averages; psutil emulates on Windows
+    mem_total_mb       int     RAM total in MiB
+    mem_used_mb        int     "used" excluding cache/buffers (psutil's notion)
+    mem_free_mb        int     psutil's available — what apps can grab now
+    mem_used_percent   float   psutil.virtual_memory().percent
+    disk_total_mb      int     home-directory partition total in MiB
+    disk_used_mb       int     home-directory partition used in MiB
+    disk_used_percent  float   home-directory partition percent
+    gpus               list    [{"name", "vram_total_mb", "vram_used_mb"}, ...]
+                               empty list when no NVIDIA GPU / nvidia-smi missing
+
+The ``gpu=False`` flag skips the ~200 ms ``nvidia-smi`` shellout for hot paths
+that don't need GPU info (e.g. 30 s heartbeats on GPU-less hosts).
+"""
+
+from __future__ import annotations
+
+import logging
+import os
+import shutil
+import subprocess
+from typing import Any
+
+import psutil as _psutil
+
+log = logging.getLogger(__name__)
+
+
+
+[docs] +def get_metrics(gpu: bool = True) -> dict[str, Any]: + """Return a flat dict of current system metrics suitable for heartbeats. + + Parameters + ---------- + gpu : bool + When ``True`` (default) probe NVIDIA GPUs via ``nvidia-smi``. Set to + ``False`` on hot paths to skip the ~200 ms shellout when you know + there's no GPU or when you cache GPU info separately. + + Returns + ------- + dict + See module docstring for the full key list and contract. + """ + metrics: dict[str, Any] = {} + + metrics["cpu_count"] = _psutil.cpu_count(logical=True) or 0 + metrics["cpu_model"] = _cpu_model() + + load1, load5, load15 = _load_avg() + metrics["load_avg_1m"] = load1 + metrics["load_avg_5m"] = load5 + metrics["load_avg_15m"] = load15 + + vm = _psutil.virtual_memory() + metrics["mem_total_mb"] = int(vm.total // (1024 * 1024)) + metrics["mem_used_mb"] = int((vm.total - vm.available) // (1024 * 1024)) + metrics["mem_free_mb"] = int(vm.available // (1024 * 1024)) + metrics["mem_used_percent"] = round(float(vm.percent), 1) + + try: + du = _psutil.disk_usage(os.path.expanduser("~")) + metrics["disk_total_mb"] = int(du.total // (1024 * 1024)) + metrics["disk_used_mb"] = int(du.used // (1024 * 1024)) + metrics["disk_used_percent"] = round(float(du.percent), 1) + except OSError: + metrics["disk_total_mb"] = 0 + metrics["disk_used_mb"] = 0 + metrics["disk_used_percent"] = 0.0 + + metrics["gpus"] = _nvidia_gpus() if gpu else [] + + return metrics
+ + + +def _cpu_model() -> str: + """Cross-platform CPU model string. Empty string if undetectable.""" + import platform + + if platform.system() == "Linux": + try: + with open("/proc/cpuinfo") as f: + for line in f: + if line.startswith("model name"): + return line.split(":", 1)[1].strip() + except OSError: + pass + if platform.system() == "Darwin": + try: + out = subprocess.check_output( + ["sysctl", "-n", "machdep.cpu.brand_string"], + text=True, + timeout=1, + ) + return out.strip() + except (OSError, subprocess.SubprocessError): + pass + return platform.processor() or "" + + +def _load_avg() -> tuple[float, float, float]: + """POSIX load averages. psutil emulates on Windows from CPU samples.""" + try: + load1, load5, load15 = _psutil.getloadavg() + return round(float(load1), 2), round(float(load5), 2), round(float(load15), 2) + except (OSError, AttributeError): + return 0.0, 0.0, 0.0 + + +def _nvidia_gpus() -> list[dict[str, Any]]: + """List of NVIDIA GPUs via nvidia-smi. Empty list if not available.""" + if not shutil.which("nvidia-smi"): + return [] + try: + out = subprocess.check_output( + [ + "nvidia-smi", + "--query-gpu=name,memory.total,memory.used", + "--format=csv,noheader,nounits", + ], + text=True, + timeout=3, + ) + except (OSError, subprocess.SubprocessError): + return [] + gpus: list[dict[str, Any]] = [] + for line in out.strip().splitlines(): + parts = [p.strip() for p in line.split(",")] + if len(parts) < 3: + continue + try: + gpus.append( + { + "name": parts[0], + "vram_total_mb": int(parts[1]), + "vram_used_mb": int(parts[2]), + } + ) + except ValueError: + continue + return gpus +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_resource/_specs/_processor_usages.html b/src/scitex/_sphinx_html/_modules/scitex_resource/_specs/_processor_usages.html new file mode 100644 index 000000000..e792e46d1 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_resource/_specs/_processor_usages.html @@ -0,0 +1,957 @@ + + + + + + + + scitex_resource._specs._processor_usages — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for scitex_resource._specs._processor_usages

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Time-stamp: "2024-11-04 16:12:50 (ywatanabe)"
+# File: ./scitex_repo/src/scitex/resource/_get_processor_usages.py
+
+"""
+Functionality:
+    * Monitors and records system resource utilization (CPU, RAM, GPU, VRAM)
+Input:
+    * None (uses system calls and psutil library)
+Output:
+    * DataFrame containing resource usage statistics
+Prerequisites:
+    * NVIDIA GPU with nvidia-smi installed
+    * psutil package
+"""
+
+import os
+import subprocess
+import sys
+from datetime import datetime
+from typing import Optional, Tuple
+
+import matplotlib.pyplot as plt
+import pandas as pd
+import psutil
+
+
+
+[docs] +def get_processor_usages() -> pd.DataFrame: + """Gets current system resource usage statistics. + + Returns + ------- + pd.DataFrame + Resource usage data with columns: + - Timestamp: Timestamp + - CPU [%]: CPU utilization + - RAM [GiB]: RAM usage + - GPU [%]: GPU utilization + - VRAM [GiB]: VRAM usage + + Example + ------- + >>> df = get_proccessor_usages() + >>> print(df) + Timestamp CPU [%] RAM [GiB] GPU [%] VRAM [GiB] + 0 2024-11-04 10:30:15 25.3 8.2 65.0 4.5 + """ + try: + cpu_perc, ram_gb = _get_cpu_usage() + gpu_perc, vram_gb = _get_gpu_usage() + + sr = pd.Series( + { + "Timestamp": datetime.now(), + "CPU [%]": cpu_perc, + "RAM [GiB]": ram_gb, + "GPU [%]": gpu_perc, + "VRAM [GiB]": vram_gb, + } + ) + + return pd.DataFrame(sr).round(1).T + except Exception as err: + raise RuntimeError(f"Failed to get resource usage: {err}")
+ + + +def _get_cpu_usage( + process: Optional[int] = os.getpid(), n_round: int = 1 +) -> Tuple[float, float]: + """Gets CPU and RAM usage statistics. + + Parameters + ---------- + process : int, optional + Process ID to monitor + n_round : int, optional + Number of decimal places to round to + + Returns + ------- + Tuple[float, float] + CPU usage percentage and RAM usage in GiB + """ + try: + cpu_usage_perc = psutil.cpu_percent() + ram_usage_gb = ( + psutil.virtual_memory().percent + / 100 + * psutil.virtual_memory().total + / (1024**3) + ) + return round(cpu_usage_perc, n_round), round(ram_usage_gb, n_round) + except Exception as err: + raise RuntimeError(f"Failed to get CPU/RAM usage: {err}") + + +def _get_gpu_usage(n_round: int = 1) -> Tuple[float, float]: + """Gets GPU and VRAM usage statistics. + + Parameters + ---------- + n_round : int, optional + Number of decimal places to round to + + Returns + ------- + Tuple[float, float] + GPU usage percentage and VRAM usage in GiB + """ + try: + result = subprocess.run( + [ + "nvidia-smi", + "--query-gpu=utilization.gpu,memory.used", + "--format=csv,nounits,noheader", + ], + capture_output=True, + text=True, + check=True, + ) + gpu_usage_perc, vram_usage_mib = result.stdout.strip().split(",") + vram_usage_gb = float(vram_usage_mib) / 1024 + return round(float(gpu_usage_perc), n_round), round(vram_usage_gb, n_round) + except: + return 0.0, 0.0 + # except subprocess.CalledProcessError as err: + # raise RuntimeError(f"Failed to execute nvidia-smi: {err}") + # except Exception as err: + # raise RuntimeError(f"Failed to get GPU/VRAM usage: {err}") + + +# def _get_gpu_usage(n_round: int = 1) -> Tuple[float, float]: +# """Gets GPU and VRAM usage statistics. + +# Parameters +# ---------- +# n_round : int, optional +# Number of decimal places to round to + +# Returns +# ------- +# Tuple[float, float] +# GPU usage percentage and VRAM usage in GiB +# """ +# try: +# result = subprocess.run( +# [ +# "nvidia-smi", +# "--query-gpu=utilization.gpu,memory.used", +# "--format=csv,nounits,noheader", +# ], +# capture_output=True, +# text=True, +# check=True, +# ) +# gpu_usage_perc, vram_usage_mib = result.stdout.strip().split(",") +# vram_usage_gb = float(vram_usage_mib) / 1024 +# return round(float(gpu_usage_perc), n_round), round(vram_usage_gb, n_round) +# except Exception as e: +# print(e) +# return 0.0, 0.0 # Return zeros when nvidia-smi is not available + + +if __name__ == "__main__": + import scitex + + CONFIG, sys.stdout, sys.stderr, plt, CC = scitex.session.start( + sys, plt, verbose=False + ) + + usage = scitex.resource.get_processor_usages() + scitex.io.save(usage, "usage.csv") + + scitex.session.close(CONFIG, verbose=False, notify=False) + +# EOF + +# #!/usr/bin/env python3 +# # -*- coding: utf-8 -*- +# # Time-stamp: "2024-11-04 10:27:35 (ywatanabe)" +# # File: ./scitex_repo/src/scitex/resource/_get_processor_usages.py + +# """ +# This script does XYZ. +# """ + +# # Functions +# import os +# import subprocess +# import sys +# from datetime import datetime + +# import matplotlib.pyplot as plt +# import scitex +# import pandas as pd +# import psutil + + +# # Functions +# def get_processor_usages(): +# """ +# Retrieves the current usage statistics for the CPU, RAM, GPU, and VRAM. + +# This function fetches the current usage percentages for the CPU and GPU, as well as the current usage in GiB for RAM and VRAM. +# The data is then compiled into a pandas DataFrame with the current timestamp. + +# Returns: +# pd.DataFrame: A pandas DataFrame containing the current usage statistics with the following columns: +# - Time: The timestamp when the data was retrieved. +# - CPU [%]: The CPU usage percentage. +# - RAM [GiB]: The RAM usage in GiB. +# - GPU [%]: The GPU usage percentage. +# - VRAM [GiB]: The VRAM usage in GiB. +# Each row in the DataFrame represents a single instance of data retrieval, rounded to 1 decimal place. + +# Example: +# >>> usage_df = get_processor_usages() +# >>> print(usage_df) +# """ +# cpu_perc, ram_gb = _get_cpu_usage() +# gpu_perc, vram_gb = _get_gpu_usage() + +# sr = pd.Series( +# { +# "Time": datetime.now(), +# "CPU [%]": cpu_perc, +# "RAM [GiB]": ram_gb, +# "GPU [%]": gpu_perc, +# "VRAM [GiB]": vram_gb, +# } +# ) + +# df = pd.DataFrame(sr).round(1).T + +# return df + + +# def _get_cpu_usage(process=os.getpid(), n_round=1): +# cpu_usage_perc = psutil.cpu_percent() +# ram_usage_gb = ( +# psutil.virtual_memory().percent +# / 100 +# * psutil.virtual_memory().total +# / (1024**3) +# ) +# return round(cpu_usage_perc, n_round), round(ram_usage_gb, n_round) + + +# def _get_gpu_usage(n_round=1): +# result = subprocess.run( +# [ +# "nvidia-smi", +# "--query-gpu=utilization.gpu,memory.used", +# "--format=csv,nounits,noheader", +# ], +# capture_output=True, +# text=True, +# ) +# gpu_usage_perc, _vram_usage_mib = result.stdout.strip().split(",") +# vram_usage_gb = float(_vram_usage_mib) / 1024 +# return round(float(gpu_usage_perc), n_round), round( +# float(vram_usage_gb), n_round +# ) + + +# # (YOUR AWESOME CODE) + +# if __name__ == "__main__": +# # Start +# CONFIG, sys.stdout, sys.stderr, plt, CC = scitex.session.start( +# sys, plt, verbose=False +# ) + +# usage = scitex.resource.get_processor_usages() +# scitex.io.save(usage, "usage.csv") + +# # Close +# scitex.session.close(CONFIG, verbose=False, notify=False) + +# + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_resource/_specs/_specs.html b/src/scitex/_sphinx_html/_modules/scitex_resource/_specs/_specs.html new file mode 100644 index 000000000..55775269e --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_resource/_specs/_specs.html @@ -0,0 +1,949 @@ + + + + + + + + scitex_resource._specs._specs — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for scitex_resource._specs._specs

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Time-stamp: "2024-11-04 14:16:49 (ywatanabe)"
+# File: ./scitex_repo/src/scitex/resource/_get_specs.py
+
+"""
+This script provides detailed system information including system basics, boot time, CPU, memory, disk, network, and custom user environment variables.
+"""
+
+import platform as _platform
+import sys
+from datetime import datetime as _datetime
+from pprint import pprint
+
+import matplotlib.pyplot as plt
+import psutil as _psutil
+import yaml as _yaml
+
+from .._compat import readable_bytes
+from .._utils._get_env_info import get_env_info
+
+
+
+[docs] +def get_specs( + system=True, + # boot_time=True, + cpu=True, + gpu=True, + disk=True, + network=True, + verbose=False, + yaml=False, +): + """ + Collects and returns system specifications including system information, CPU, GPU, disk, and network details. + + This function gathers various pieces of system information based on the parameters provided. It can return the data in a dictionary format or print it out based on the verbose flag. Additionally, there's an option to format the output as YAML. + + Arguments: + system (bool): If True, collects system-wide information such as OS and node name. Default is True. + boot_time (bool): If True, collects system boot time. Currently commented out in the implementation. Default is True. + cpu (bool): If True, collects CPU-specific information including frequency and usage. Default is True. + gpu (bool): If True, collects GPU-specific information. Default is True. + disk (bool): If True, collects disk usage information for all partitions. Default is True. + network (bool): If True, collects network interface and traffic information. Default is True. + verbose (bool): If True, prints the collected information using pprint. Default is False. + yaml (bool): If True, formats the collected information as YAML. This modifies the return type to a YAML formatted string. Default is False. + + Returns: + dict or str: By default, returns a dictionary containing the collected system specifications. If `yaml` is True, returns a YAML-formatted string instead. + + Note: + - The actual collection of system, CPU, GPU, disk, and network information depends on the availability of corresponding libraries and access permissions. + - The `boot_time` argument is currently not used as its corresponding code is commented out. + - The function uses global variables and imports within its scope, which might affect its reusability and testability. + + Example: + >>> specs = get_specs(verbose=True) + This will print and return the system specifications based on the default parameters. + + Dependencies: + - This function depends on the `scitex` library for accessing system information and formatting output. Ensure this library is installed and properly configured. + - Python standard libraries: `datetime`, `platform`, `psutil`, `yaml` (optional for YAML output). + + Raises: + PermissionError: If the function lacks necessary permissions to access certain system information, especially disk and network details. + """ + + # To prevent import errors, _SUPPLE_INFO is collected here. + global _SUPPLE_INFO + _SUPPLE_INFO = get_env_info()._asdict() + + collected_info = {} # OrderedDict() + + collected_info["Collected Time"] = _datetime.now().strftime("%Y-%m-%d %H:%M:%S") + if system: + collected_info["System Information"] = _system_info() + # if boot_time: + # collected_info["Boot Time"] = _boot_time_info() + if cpu: + collected_info["CPU Info"] = _cpu_info() + collected_info["Memory Info"] = _memory_info() + if gpu: + collected_info["GPU Info"] = _supple_nvidia_info() + # scitex.gen.placeholder() + if disk: + collected_info["Disk Info"] = _disk_info() + if network: + collected_info["Network Info"] = _network_info() + + if yaml: + collected_info = _yaml.dump(collected_info, sort_keys=False) + + if verbose: + pprint(collected_info) + + return collected_info
+ + + +def _system_info(): + uname = _platform.uname() + return { + "OS": _supple_os_info()["os"], + # "GCC version": _supple_os_info()["gcc_version"], + # "System": uname.system, + "Node Name": uname.node, + "Release": uname.release, + "Version": uname.version, + # "Machine": uname.machine, + # "Processor": uname.processor, + } + + +# def _boot_time_info(): +# boot_time_timestamp = _psutil.boot_time() +# bt = _datetime.fromtimestamp(boot_time_timestamp) +# return { +# "Boot Time": f"{bt.year}-{bt.month:02d}-{bt.day:02d} {bt.hour:02d}:{bt.minute:02d}:{bt.second:02d}" +# } + + +def _cpu_info(): + cpufreq = _psutil.cpu_freq() + cpu_usage_per_core = _psutil.cpu_percent(percpu=True, interval=1) + return { + "Physical cores": _psutil.cpu_count(logical=False), + "Total cores": _psutil.cpu_count(logical=True), + "Max Frequency": f"{cpufreq.max:.2f} MHz", + "Min Frequency": f"{cpufreq.min:.2f} MHz", + "Current Frequency": f"{cpufreq.current:.2f} MHz", + "CPU Usage Per Core": { + f"Core {i}": f"{percentage}%" + for i, percentage in enumerate(cpu_usage_per_core) + }, + "Total CPU Usage": f"{_psutil.cpu_percent()}%", + } + + +def _memory_info(): + svmem = _psutil.virtual_memory() + swap = _psutil.swap_memory() + + return { + "Memory": { + "Total": readable_bytes(svmem.total), + "Available": readable_bytes(svmem.available), + "Used": readable_bytes(svmem.used), + "Percentage": svmem.percent, + }, + "SWAP": { + "Total": readable_bytes(swap.total), + "Free": readable_bytes(swap.free), + "Used": readable_bytes(swap.used), + "Percentage": swap.percent, + }, + } + + +def _disk_info(): + partitions_info = {} + partitions = _psutil.disk_partitions() + for partition in partitions: + try: + usage = _psutil.disk_usage(partition.mountpoint) + partitions_info[partition.device] = { + "Mountpoint": partition.mountpoint, + "File system type": partition.fstype, + "Total Size": readable_bytes(usage.total), + "Used": readable_bytes(usage.used), + "Free": readable_bytes(usage.free), + "Percentage": usage.percent, + } + except PermissionError: + continue + + disk_io = _psutil.disk_io_counters() + return { + "Partitions": partitions_info, + "Total read": readable_bytes(disk_io.read_bytes), + "Total write": readable_bytes(disk_io.write_bytes), + } + + +def _network_info(): + if_addrs = _psutil.net_if_addrs() + interfaces = {} + for interface_name, interface_addresses in if_addrs.items(): + interface_info = [] + for address in interface_addresses: + interface_info.append( + { + # "Address Type": "IP" if address.family == _psutil.AF_INET else "MAC", + "Address": address.address, + "Netmask": address.netmask, + "Broadcast": address.broadcast, + } + ) + interfaces[interface_name] = interface_info + + net_io = _psutil.net_io_counters() + return { + "Interfaces": interfaces, + "Total Sent": readable_bytes(net_io.bytes_sent), + "Total Received": readable_bytes(net_io.bytes_recv), + } + + +def _python_info(): + return _supple_python_info() + + +def _supple_os_info(): + _SUPPLE_OS_KEYS = [ + "os", + "gcc_version", + ] + return {k: _SUPPLE_INFO[k] for k in _SUPPLE_OS_KEYS} + + +def _supple_python_info(): + _SUPPLE_PYTHON_KEYS = [ + "python_version", + "torch_version", + "is_cuda_available", + "pip_version", + "pip_packages", + "conda_packages", + ] + + return {k: _SUPPLE_INFO[k] for k in _SUPPLE_PYTHON_KEYS} + + +def _supple_nvidia_info(): + _SUPPLE_NVIDIA_KEYS = [ + "nvidia_gpu_models", + "nvidia_driver_version", + "cuda_runtime_version", + "cudnn_version", + ] + + def replace_key(key): + return key + + def replace_key(key): + key = key.replace("_", " ") + key = key.replace("nvidia", "NVIDIA") + key = key.replace("gpu", "GPU") + key = key.replace("cuda", "CUDA") + key = key.replace("cudnn", "cuDNN") + key = key.replace("driver", "Driver") + key = key.replace("runtime", "Runtime") + return key + + return {replace_key(k): _SUPPLE_INFO[k] for k in _SUPPLE_NVIDIA_KEYS} + + +if __name__ == "__main__": + import scitex + + # Start + CONFIG, sys.stdout, sys.stderr, plt, CC = scitex.session.start(sys, plt) + + info = scitex.res.get_specs() + scitex.io.save(info, "specs.yaml") + + # Close + scitex.session.close(CONFIG) + +# EOF + +""" +/home/ywatanabe/proj/entrance/scitex/res/_get_specs.py +""" + + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_security/github.html b/src/scitex/_sphinx_html/_modules/scitex_security/github.html new file mode 100644 index 000000000..89352fa05 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_security/github.html @@ -0,0 +1,1131 @@ + + + + + + + + scitex_security.github — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_security.github

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# File: ~/proj/scitex-code/src/scitex/security/github.py
+
+"""
+GitHub Security Alerts Module
+
+Fetches and processes security alerts from GitHub.
+
+Collaborator injection
+----------------------
+Per the SciTeX no-mocks rule (PA-306), the production callables that
+talk to external collaborators (the ``subprocess`` module, and the
+in-module ``_run_gh_command``/``check_gh_auth``/``get_*_alerts``
+helpers) accept keyword-only overrides defaulting to the real module
+globals. Tests pass real hand-rolled fakes; production code does not
+pass anything.
+"""
+
+import json
+import subprocess
+from datetime import datetime
+from pathlib import Path
+from typing import Callable, Dict, List, Optional
+
+
+
+[docs] +class GitHubSecurityError(Exception): + """Raised when GitHub security operations fail.""" + + pass
+ + + +def _run_gh_command( + args: List[str], + *, + run: Optional[Callable] = None, +) -> str: + """Run GitHub CLI command and return output. + + Args: + args: Arguments to pass to ``gh`` (without the ``gh`` prefix). + run: ``subprocess.run``-shaped callable. Defaults to the real + ``subprocess.run``. Override in tests. + """ + if run is None: + run = subprocess.run + try: + result = run( + ["gh"] + args, + capture_output=True, + text=True, + check=True, + ) + return result.stdout + except subprocess.CalledProcessError as e: + raise GitHubSecurityError(f"GitHub CLI error: {e.stderr}") + except FileNotFoundError: + raise GitHubSecurityError( + "GitHub CLI (gh) not found. Install: https://cli.github.com/" + ) + + +def check_gh_auth(*, run: Optional[Callable] = None) -> bool: + """Check if GitHub CLI is authenticated. + + Args: + run: ``subprocess.run``-shaped callable. Defaults to the real + ``subprocess.run``. Override in tests. + """ + if run is None: + run = subprocess.run + try: + run( + ["gh", "auth", "status"], + capture_output=True, + check=True, + ) + return True + except (subprocess.CalledProcessError, FileNotFoundError): + return False + + +def get_secret_alerts( + repo: Optional[str] = None, + *, + gh_runner: Optional[Callable[[List[str]], str]] = None, +) -> List[Dict]: + """ + Get secret scanning alerts. + + Args: + repo: Repository in format 'owner/repo'. If None, uses current repo. + gh_runner: ``_run_gh_command``-shaped callable. Defaults to + :func:`_run_gh_command`. Override in tests. + + Returns: + List of secret scanning alerts + """ + if gh_runner is None: + gh_runner = _run_gh_command + try: + # Use GitHub REST API for secret scanning + api_path = "/repos/:owner/:repo/secret-scanning/alerts" + if repo: + owner, repo_name = repo.split("/") + api_path = f"/repos/{owner}/{repo_name}/secret-scanning/alerts" + + output = gh_runner( + [ + "api", + api_path, + "--paginate", + "--jq", + ".[] | {state, secretType: .secret_type_display_name, " + "url: .html_url, " + "createdAt: .created_at, " + "path: .first_location_detected.path, " + "line: .first_location_detected.start_line}", + ] + ) + + if not output.strip(): + return [] + + # Parse line-delimited JSON + alerts = [] + for line in output.strip().split("\n"): + if line.strip(): + alerts.append(json.loads(line)) + return alerts + except GitHubSecurityError: + return [] + + +def get_dependabot_alerts( + repo: Optional[str] = None, + *, + gh_runner: Optional[Callable[[List[str]], str]] = None, +) -> List[Dict]: + """ + Get Dependabot vulnerability alerts. + + Args: + repo: Repository in format 'owner/repo'. If None, uses current repo. + gh_runner: ``_run_gh_command``-shaped callable. Defaults to + :func:`_run_gh_command`. Override in tests. + + Returns: + List of Dependabot alerts + """ + if gh_runner is None: + gh_runner = _run_gh_command + try: + # Use GitHub API to get Dependabot alerts + api_path = "/repos/:owner/:repo/dependabot/alerts" + if repo: + owner, repo_name = repo.split("/") + api_path = f"/repos/{owner}/{repo_name}/dependabot/alerts" + + output = gh_runner( + [ + "api", + api_path, + "--paginate", + "--jq", + ".[] | {state, severity: .security_advisory.severity, " + "summary: .security_advisory.summary, " + "package: .dependency.package.name, " + "cve: .security_advisory.cve_id, " + "url: .html_url, " + "created_at: .created_at}", + ] + ) + + if not output.strip(): + return [] + + # Parse line-delimited JSON + alerts = [] + for line in output.strip().split("\n"): + if line.strip(): + alerts.append(json.loads(line)) + return alerts + except GitHubSecurityError: + return [] + + +def get_code_scanning_alerts( + repo: Optional[str] = None, + *, + gh_runner: Optional[Callable[[List[str]], str]] = None, +) -> List[Dict]: + """ + Get code scanning alerts. + + Args: + repo: Repository in format 'owner/repo'. If None, uses current repo. + gh_runner: ``_run_gh_command``-shaped callable. Defaults to + :func:`_run_gh_command`. Override in tests. + + Returns: + List of code scanning alerts + """ + if gh_runner is None: + gh_runner = _run_gh_command + try: + # Use GitHub API to get code scanning alerts + api_path = "/repos/:owner/:repo/code-scanning/alerts" + if repo: + owner, repo_name = repo.split("/") + api_path = f"/repos/{owner}/{repo_name}/code-scanning/alerts" + + output = gh_runner( + [ + "api", + api_path, + "--paginate", + "--jq", + ".[] | {state, severity: .rule.severity, " + "description: .rule.description, " + "location: .most_recent_instance.location.path, " + "line: .most_recent_instance.location.start_line, " + "url: .html_url, " + "created_at: .created_at}", + ] + ) + + if not output.strip(): + return [] + + # Parse line-delimited JSON + alerts = [] + for line in output.strip().split("\n"): + if line.strip(): + alerts.append(json.loads(line)) + return alerts + except GitHubSecurityError: + return [] + + +
+[docs] +def check_github_alerts( + repo: Optional[str] = None, + *, + auth_check: Optional[Callable[[], bool]] = None, + secrets_fn: Optional[Callable] = None, + dependabot_fn: Optional[Callable] = None, + code_scanning_fn: Optional[Callable] = None, +) -> Dict[str, List[Dict]]: + """ + Check all GitHub security alerts. + + Args: + repo: Repository in format 'owner/repo'. If None, uses current repo. + auth_check: ``check_gh_auth``-shaped callable. Override in tests. + secrets_fn: ``get_secret_alerts``-shaped callable. Override in tests. + dependabot_fn: ``get_dependabot_alerts``-shaped callable. Override in + tests. + code_scanning_fn: ``get_code_scanning_alerts``-shaped callable. + Override in tests. + + Returns: + Dictionary with keys: 'secrets', 'dependabot', 'code_scanning' + + Raises: + GitHubSecurityError: If GitHub CLI is not installed or not authenticated + """ + if auth_check is None: + auth_check = check_gh_auth + if secrets_fn is None: + secrets_fn = get_secret_alerts + if dependabot_fn is None: + dependabot_fn = get_dependabot_alerts + if code_scanning_fn is None: + code_scanning_fn = get_code_scanning_alerts + + if not auth_check(): + raise GitHubSecurityError( + "Not authenticated with GitHub CLI. Run: gh auth login" + ) + + return { + "secrets": secrets_fn(repo), + "dependabot": dependabot_fn(repo), + "code_scanning": code_scanning_fn(repo), + }
+ + + +
+[docs] +def format_alerts_report(alerts: Dict[str, List[Dict]]) -> str: + """ + Format alerts into a readable text report. + + Args: + alerts: Dictionary of alerts from check_github_alerts() + + Returns: + Formatted text report + """ + lines = [] + lines.append("=" * 50) + lines.append("GitHub Security Alerts Report") + lines.append(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") + lines.append("=" * 50) + lines.append("") + + # Secret scanning alerts + lines.append("### SECRET SCANNING ALERTS ###") + lines.append("") + secrets = [s for s in alerts["secrets"] if s.get("state") == "open"] + if secrets: + for alert in secrets: + lines.append(f"- [{alert['state']}] {alert['secretType']}") + path = alert.get("path", "N/A") + line_num = alert.get("line", "") + if path != "N/A" and line_num: + lines.append(f" Location: {path}:{line_num}") + lines.append(f" Created: {alert.get('createdAt', 'N/A')}") + lines.append(f" URL: {alert['url']}") + lines.append("") + else: + lines.append("No open secret scanning alerts") + lines.append("") + + lines.append("=" * 50) + lines.append("") + + # Dependabot alerts + lines.append("### DEPENDABOT VULNERABILITY ALERTS ###") + lines.append("") + dependabot = [d for d in alerts["dependabot"] if d.get("state") == "open"] + if dependabot: + for alert in dependabot: + severity = alert.get("severity", "unknown").upper() + lines.append(f"- [{alert['state']}] {severity}: {alert['summary']}") + lines.append(f" Package: {alert['package']}") + lines.append(f" CVE: {alert.get('cve') or 'N/A'}") + lines.append(f" URL: {alert['url']}") + lines.append("") + else: + lines.append("No open Dependabot alerts") + lines.append("") + + lines.append("=" * 50) + lines.append("") + + # Code scanning alerts + lines.append("### CODE SCANNING ALERTS ###") + lines.append("") + code_scanning = [c for c in alerts["code_scanning"] if c.get("state") == "open"] + if code_scanning: + for alert in code_scanning: + severity = alert.get("severity", "unknown").upper() + lines.append(f"- [{alert['state']}] {severity}: {alert['description']}") + location = alert.get("location", "N/A") + line_num = alert.get("line", "") + if line_num: + location = f"{location}:{line_num}" + lines.append(f" Location: {location}") + lines.append(f" URL: {alert['url']}") + lines.append("") + else: + lines.append("No open code scanning alerts") + lines.append("") + + lines.append("=" * 50) + lines.append("") + + # Summary + total = len(secrets) + len(dependabot) + len(code_scanning) + lines.append("### SUMMARY ###") + lines.append("") + lines.append(f"Total open alerts: {total}") + lines.append(f" - Secrets: {len(secrets)}") + lines.append(f" - Dependabot: {len(dependabot)}") + lines.append(f" - Code Scanning: {len(code_scanning)}") + lines.append("") + + if total > 0: + lines.append("⚠️ ACTION REQUIRED: Security issues found!") + else: + lines.append("✓ No open security alerts") + + return "\n".join(lines)
+ + + +
+[docs] +def save_alerts_to_file( + alerts: Dict[str, List[Dict]], + output_dir: Optional[Path] = None, + create_symlink: bool = True, +) -> Path: + """ + Save alerts to a timestamped file. + + Args: + alerts: Dictionary of alerts from check_github_alerts() + output_dir: Directory to save file. Defaults to ./logs/security + create_symlink: If True, create 'security-latest.txt' symlink + + Returns: + Path to saved file + """ + if output_dir is None: + output_dir = Path.cwd() / "logs" / "security" + else: + output_dir = Path(output_dir) + + output_dir.mkdir(parents=True, exist_ok=True) + + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + output_file = output_dir / f"security-{timestamp}.txt" + + report = format_alerts_report(alerts) + output_file.write_text(report) + + # Create symlink to latest + if create_symlink: + latest_link = output_dir / "security-latest.txt" + if latest_link.exists() or latest_link.is_symlink(): + latest_link.unlink() + latest_link.symlink_to(output_file.name) + + return output_file
+ + + +
+[docs] +def get_latest_alerts_file(security_dir: Optional[Path] = None) -> Optional[Path]: + """ + Get path to the latest security alerts file. + + Args: + security_dir: Directory containing security files. Defaults to ./logs/security + + Returns: + Path to latest file, or None if not found + """ + if security_dir is None: + security_dir = Path.cwd() / "logs" / "security" + else: + security_dir = Path(security_dir) + + latest_link = security_dir / "security-latest.txt" + if latest_link.exists(): + return latest_link + + # Fallback: find most recent file + files = sorted(security_dir.glob("security-*.txt"), reverse=True) + return files[0] if files else None
+ +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_ui.html b/src/scitex/_sphinx_html/_modules/scitex_ui.html new file mode 100644 index 000000000..63e3b5dc5 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_ui.html @@ -0,0 +1,741 @@ + + + + + + + + scitex_ui — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_ui

+#!/usr/bin/env python3
+"""scitex-ui — Shared frontend UI components for the SciTeX ecosystem.
+
+Components ship as TypeScript + CSS static assets, discoverable by Django's
+AppDirectoriesFinder when added to INSTALLED_APPS.
+
+Python API provides component metadata and registration.
+"""
+
+from __future__ import annotations
+
+try:
+    from importlib.metadata import version as _v, PackageNotFoundError
+    try:
+        __version__ = _v("scitex-ui")
+    except PackageNotFoundError:
+        __version__ = "0.0.0+local"
+    del _v, PackageNotFoundError
+except ImportError:  # pragma: no cover — only on ancient Pythons
+    __version__ = "0.0.0+local"
+from pathlib import Path as _Path
+
+from ._registry import get_component, list_components
+from ._registry import register_component as _register_component
+from . import _components  # noqa: F401 — triggers registration
+
+# Advanced: re-export for custom component authors
+register_component = _register_component
+
+
+
+[docs] +def get_static_dir() -> _Path: + """Return the absolute path to scitex_ui's static asset directory. + + This is the directory containing ``css/`` and ``ts/`` subdirectories. + Works for both pip-installed packages and editable (dev) installs. + + Useful for build tools (Vite, Webpack) that need to resolve + scitex-ui source files at build time. + + Returns + ------- + pathlib.Path + e.g. ``/usr/lib/python3.11/.../scitex_ui/static/scitex_ui`` + """ + return _Path(__file__).parent / "static" / "scitex_ui"
+ + + +
+[docs] +def get_docs_path() -> _Path: + """Return the absolute path to scitex_ui's bundled documentation directory. + + The directory contains ``APP_DEVELOPER_GUIDE.md`` and the Sphinx-built + HTML docs. Works for both pip-installed packages and editable (dev) installs. + + Returns + ------- + pathlib.Path + e.g. ``/usr/lib/python3.11/.../scitex_ui/_docs`` + """ + return _Path(__file__).parent / "_sphinx_html"
+ + + +__all__ = ["__version__", "get_component", "list_components", "get_static_dir", "get_docs_path"] + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_ui/_registry.html b/src/scitex/_sphinx_html/_modules/scitex_ui/_registry.html new file mode 100644 index 000000000..3f6178be5 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_ui/_registry.html @@ -0,0 +1,700 @@ + + + + + + + + scitex_ui._registry — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_ui._registry

+#!/usr/bin/env python3
+"""Component metadata registry."""
+
+from __future__ import annotations
+from typing import Any, Optional
+
+_COMPONENTS: dict[str, Any] = {}
+
+
+def register_component(name: str, metadata: Any) -> None:
+    """Register a frontend component's metadata."""
+    _COMPONENTS[name] = metadata
+
+
+
+[docs] +def get_component(name: str) -> Optional[Any]: + """Get metadata for a registered component.""" + return _COMPONENTS.get(name)
+ + + +
+[docs] +def list_components() -> list[str]: + """List all registered component names.""" + return sorted(_COMPONENTS.keys())
+ +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_writer/_dataclasses/results/_CompilationResult.html b/src/scitex/_sphinx_html/_modules/scitex_writer/_dataclasses/results/_CompilationResult.html new file mode 100644 index 000000000..3f4093e6c --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_writer/_dataclasses/results/_CompilationResult.html @@ -0,0 +1,745 @@ + + + + + + + + scitex_writer._dataclasses.results._CompilationResult — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for scitex_writer._dataclasses.results._CompilationResult

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# File: src/scitex_writer/_dataclasses/results/_CompilationResult.py
+
+"""
+CompilationResult - dataclass for LaTeX compilation results.
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass, field
+from pathlib import Path
+from typing import List, Optional
+
+
+
+[docs] +@dataclass +class CompilationResult: + """Result of LaTeX compilation.""" + + success: bool + """Whether compilation succeeded (exit code 0)""" + + exit_code: int + """Process exit code""" + + stdout: str + """Standard output from compilation""" + + stderr: str + """Standard error from compilation""" + + output_pdf: Optional[Path] = None + """Path to generated PDF (if successful)""" + + diff_pdf: Optional[Path] = None + """Path to diff PDF with tracked changes (if generated)""" + + log_file: Optional[Path] = None + """Path to compilation log file""" + + duration: float = 0.0 + """Compilation duration in seconds""" + + errors: List[str] = field(default_factory=list) + """Parsed LaTeX errors (if any)""" + + warnings: List[str] = field(default_factory=list) + """Parsed LaTeX warnings (if any)""" + +
+[docs] + def __str__(self): + """Human-readable summary.""" + status = "SUCCESS" if self.success else "FAILED" + lines = [ + f"Compilation {status} (exit code: {self.exit_code})", + f"Duration: {self.duration:.2f}s", + ] + if self.output_pdf: + lines.append(f"Output: {self.output_pdf}") + if self.errors: + lines.append(f"Errors: {len(self.errors)}") + if self.warnings: + lines.append(f"Warnings: {len(self.warnings)}") + return "\n".join(lines)
+
+ + + +__all__ = ["CompilationResult"] + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_writer/_dataclasses/tree/_ManuscriptTree.html b/src/scitex/_sphinx_html/_modules/scitex_writer/_dataclasses/tree/_ManuscriptTree.html new file mode 100644 index 000000000..40d372a49 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_writer/_dataclasses/tree/_ManuscriptTree.html @@ -0,0 +1,758 @@ + + + + + + + + scitex_writer._dataclasses.tree._ManuscriptTree — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for scitex_writer._dataclasses.tree._ManuscriptTree

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# File: src/scitex_writer/_dataclasses/tree/_ManuscriptTree.py
+
+"""
+ManuscriptTree - dataclass for manuscript directory structure.
+
+Represents the 01_manuscript/ directory with all subdirectories.
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass
+from pathlib import Path
+from typing import Optional
+
+from ..contents import ManuscriptContents
+from ..core import DocumentSection
+
+
+
+[docs] +@dataclass +class ManuscriptTree: + """Manuscript directory structure (01_manuscript/).""" + + root: Path + git_root: Optional[Path] = None + + # Contents subdirectory + contents: ManuscriptContents = None + + # Root level files + base: DocumentSection = None + readme: DocumentSection = None + + # Directories + archive: Path = None + +
+[docs] + def __post_init__(self): + """Initialize all instances.""" + if self.contents is None: + self.contents = ManuscriptContents(self.root / "contents", self.git_root) + if self.base is None: + self.base = DocumentSection(self.root / "base.tex", self.git_root) + if self.readme is None: + self.readme = DocumentSection(self.root / "README.md", self.git_root) + if self.archive is None: + self.archive = self.root / "archive"
+ + +
+[docs] + def verify_structure(self) -> tuple[bool, list[str]]: + """ + Verify manuscript structure has required components. + + Returns: + (is_valid, list_of_missing_items_with_paths) + """ + missing = [] + + # Check contents structure + contents_valid, contents_missing = self.contents.verify_structure() + if not contents_valid: + # Contents already includes full paths, just pass them through + missing.extend(contents_missing) + + # Check root level files + if not self.base.path.exists(): + expected_path = ( + self.base.path.relative_to(self.git_root) + if self.git_root + else self.base.path + ) + missing.append(f"base.tex (expected at: {expected_path})") + + return len(missing) == 0, missing
+
+ + + +__all__ = ["ManuscriptTree"] + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_writer/_dataclasses/tree/_RevisionTree.html b/src/scitex/_sphinx_html/_modules/scitex_writer/_dataclasses/tree/_RevisionTree.html new file mode 100644 index 000000000..638738086 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_writer/_dataclasses/tree/_RevisionTree.html @@ -0,0 +1,771 @@ + + + + + + + + scitex_writer._dataclasses.tree._RevisionTree — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for scitex_writer._dataclasses.tree._RevisionTree

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# File: src/scitex_writer/_dataclasses/tree/_RevisionTree.py
+
+"""
+RevisionTree - dataclass for revision directory structure.
+
+Represents the 03_revision/ directory with all subdirectories.
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass
+from pathlib import Path
+from typing import Optional
+
+from ..contents import RevisionContents
+from ..core import DocumentSection
+
+
+
+[docs] +@dataclass +class RevisionTree: + """Revision directory structure (03_revision/).""" + + root: Path + git_root: Optional[Path] = None + + # Contents subdirectory + contents: RevisionContents = None + + # Root level files + base: DocumentSection = None + revision: DocumentSection = None + readme: DocumentSection = None + + # Directories + archive: Path = None + docs: Path = None + +
+[docs] + def __post_init__(self): + """Initialize all instances.""" + if self.contents is None: + self.contents = RevisionContents(self.root / "contents", self.git_root) + if self.base is None: + self.base = DocumentSection(self.root / "base.tex", self.git_root) + if self.revision is None: + self.revision = DocumentSection(self.root / "revision.tex", self.git_root) + if self.readme is None: + self.readme = DocumentSection(self.root / "README.md", self.git_root) + if self.archive is None: + self.archive = self.root / "archive" + if self.docs is None: + self.docs = self.root / "docs"
+ + +
+[docs] + def verify_structure(self) -> tuple[bool, list[str]]: + """ + Verify revision structure has required components. + + Returns: + (is_valid, list_of_missing_items_with_paths) + """ + missing = [] + + # Check contents structure + contents_valid, contents_issues = self.contents.verify_structure() + if not contents_valid: + # Contents already includes full paths, just pass them through + missing.extend(contents_issues) + + # Check root level files + if not self.base.path.exists(): + expected_path = ( + self.base.path.relative_to(self.git_root) + if self.git_root + else self.base.path + ) + missing.append(f"base.tex (expected at: {expected_path})") + if not self.revision.path.exists(): + expected_path = ( + self.revision.path.relative_to(self.git_root) + if self.git_root + else self.revision.path + ) + missing.append(f"revision.tex (expected at: {expected_path})") + + return len(missing) == 0, missing
+
+ + + +__all__ = ["RevisionTree"] + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_writer/_dataclasses/tree/_SupplementaryTree.html b/src/scitex/_sphinx_html/_modules/scitex_writer/_dataclasses/tree/_SupplementaryTree.html new file mode 100644 index 000000000..d6e3a7426 --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_writer/_dataclasses/tree/_SupplementaryTree.html @@ -0,0 +1,775 @@ + + + + + + + + scitex_writer._dataclasses.tree._SupplementaryTree — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for scitex_writer._dataclasses.tree._SupplementaryTree

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# File: src/scitex_writer/_dataclasses/tree/_SupplementaryTree.py
+
+"""
+SupplementaryTree - dataclass for supplementary directory structure.
+
+Represents the 02_supplementary/ directory with all subdirectories.
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass
+from pathlib import Path
+from typing import Optional
+
+from ..contents import SupplementaryContents
+from ..core import DocumentSection
+
+
+
+[docs] +@dataclass +class SupplementaryTree: + """Supplementary directory structure (02_supplementary/).""" + + root: Path + git_root: Optional[Path] = None + + # Contents subdirectory + contents: SupplementaryContents = None + + # Root level files + base: DocumentSection = None + supplementary: DocumentSection = None + supplementary_diff: DocumentSection = None + readme: DocumentSection = None + + # Directories + archive: Path = None + +
+[docs] + def __post_init__(self): + """Initialize all instances.""" + if self.contents is None: + self.contents = SupplementaryContents(self.root / "contents", self.git_root) + if self.base is None: + self.base = DocumentSection(self.root / "base.tex", self.git_root) + if self.supplementary is None: + self.supplementary = DocumentSection( + self.root / "supplementary.tex", self.git_root + ) + if self.supplementary_diff is None: + self.supplementary_diff = DocumentSection( + self.root / "supplementary_diff.tex", self.git_root + ) + if self.readme is None: + self.readme = DocumentSection(self.root / "README.md", self.git_root) + if self.archive is None: + self.archive = self.root / "archive"
+ + +
+[docs] + def verify_structure(self) -> tuple[bool, list[str]]: + """ + Verify supplementary structure has required components. + + Returns: + (is_valid, list_of_missing_items_with_paths) + """ + missing = [] + + # Check contents structure + contents_valid, contents_issues = self.contents.verify_structure() + if not contents_valid: + # Contents already includes full paths, just pass them through + missing.extend(contents_issues) + + # Check root level files + if not self.base.path.exists(): + expected_path = ( + self.base.path.relative_to(self.git_root) + if self.git_root + else self.base.path + ) + missing.append(f"base.tex (expected at: {expected_path})") + if not self.supplementary.path.exists(): + expected_path = ( + self.supplementary.path.relative_to(self.git_root) + if self.git_root + else self.supplementary.path + ) + missing.append(f"supplementary.tex (expected at: {expected_path})") + + return len(missing) == 0, missing
+
+ + + +__all__ = ["SupplementaryTree"] + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_modules/scitex_writer/writer.html b/src/scitex/_sphinx_html/_modules/scitex_writer/writer.html new file mode 100644 index 000000000..076a9d8dc --- /dev/null +++ b/src/scitex/_sphinx_html/_modules/scitex_writer/writer.html @@ -0,0 +1,1056 @@ + + + + + + + + scitex_writer.writer — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for scitex_writer.writer

+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# File: src/scitex_writer/writer.py
+
+"""
+Writer class for manuscript LaTeX compilation.
+
+Provides object-oriented interface to scitex-writer functionality.
+"""
+
+from __future__ import annotations
+
+import logging
+import shutil
+import subprocess
+from pathlib import Path
+from typing import Callable, Optional
+
+from ._compile import (
+    CompilationResult,
+    compile_manuscript,
+    compile_revision,
+    compile_supplementary,
+)
+from ._dataclasses import ManuscriptTree, RevisionTree, SupplementaryTree
+from ._dataclasses.config import DOC_TYPE_DIRS
+from ._dataclasses.tree import ScriptsTree, SharedTree
+from ._project._create import clone_writer_project
+from ._utils._watch import watch_manuscript
+
+logger = logging.getLogger(__name__)
+
+
+def _find_git_root(project_dir: Path) -> Optional[Path]:
+    """Find git root for project directory."""
+    try:
+        result = subprocess.run(
+            ["git", "rev-parse", "--show-toplevel"],
+            cwd=str(project_dir),
+            capture_output=True,
+            text=True,
+            timeout=5,
+        )
+        if result.returncode == 0:
+            return Path(result.stdout.strip())
+    except Exception:
+        pass
+    return None
+
+
+
+[docs] +class Writer: + """LaTeX manuscript compiler.""" + +
+[docs] + def __init__( + self, + project_dir: Path, + name: Optional[str] = None, + git_strategy: Optional[str] = "child", + branch: Optional[str] = None, + tag: Optional[str] = None, + ): + """ + Initialize for project directory. + + If directory doesn't exist, creates new project. + + Parameters + ---------- + project_dir : Path + Path to project directory. + name : str, optional + Project name (used if creating new project). + git_strategy : str, optional + Git initialization strategy: + - 'child': Create isolated git in project directory (default) + - 'parent': Use parent git repository + - 'origin': Preserve template's original git history + - None or 'none': Disable git initialization + branch : str, optional + Specific branch of template repository to clone. + If None, clones the default branch. Mutually exclusive with tag. + tag : str, optional + Specific tag/release of template repository to clone. + If None, clones the default branch. Mutually exclusive with branch. + """ + self.project_name = name or Path(project_dir).name + self.project_dir = Path(project_dir) + self.git_strategy = git_strategy + self.branch = branch + self.tag = tag + + ref_info = "" + if branch: + ref_info = f" (branch: {branch})" + elif tag: + ref_info = f" (tag: {tag})" + logger.info( + f"Writer: Initializing with:\n" + f" Project Name: {self.project_name}\n" + f" Project Directory: {self.project_dir.absolute()}\n" + f" Git Strategy: {self.git_strategy}{ref_info}..." + ) + + # Create or attach to project + self.project_dir = self._attach_or_create_project(name) + + # Find git root (may be the project dir or a parent) + self.git_root = _find_git_root(self.project_dir) or self.project_dir + + # Document accessors (pass git_root for efficiency) + self.shared = SharedTree(self.project_dir / "00_shared", git_root=self.git_root) + self.manuscript = ManuscriptTree( + self.project_dir / "01_manuscript", git_root=self.git_root + ) + self.supplementary = SupplementaryTree( + self.project_dir / "02_supplementary", git_root=self.git_root + ) + self.revision = RevisionTree( + self.project_dir / "03_revision", git_root=self.git_root + ) + self.scripts = ScriptsTree(self.project_dir / "scripts", git_root=self.git_root) + + logger.info(f"Writer: Initialization complete for {self.project_name}")
+ + + def _attach_or_create_project(self, name: Optional[str] = None) -> Path: + """ + Create new project or attach to existing one. + + If project directory doesn't exist, creates it based on git_strategy: + - 'child': Full template with git initialization + - 'parent'/'None': Minimal directory structure + + Parameters + ---------- + name : str, optional + Project name (used if creating new project). + + Returns + ------- + Path + Path to the project directory. + """ + if self.project_dir.exists(): + logger.info( + f"Writer: Attached to existing project at {self.project_dir.absolute()}" + ) + # Verify existing project structure + self._verify_project_structure() + return self.project_dir + + project_name = name or self.project_dir.name + + logger.info( + f"Writer: Creating new project '{project_name}' at {self.project_dir.absolute()}" + ) + + # Initialize project directory structure + success = clone_writer_project( + str(self.project_dir), self.git_strategy, self.branch, self.tag + ) + + if not success: + logger.error( + f"Writer: Failed to initialize project directory for {project_name}" + ) + raise RuntimeError( + f"Could not create project directory at {self.project_dir}" + ) + + # Verify project directory was created + if not self.project_dir.exists(): + logger.error( + f"Writer: Project directory {self.project_dir} was not created" + ) + raise RuntimeError(f"Project directory {self.project_dir} was not created") + + logger.info( + f"Writer: Successfully created project at {self.project_dir.absolute()}" + ) + return self.project_dir + + def _verify_project_structure(self) -> None: + """ + Verify attached project has expected structure. + + Checks: + - Required directories exist (01_manuscript, 02_supplementary, 03_revision) + + Raises + ------ + RuntimeError + If structure is invalid. + """ + required_dirs = [ + self.project_dir / "01_manuscript", + self.project_dir / "02_supplementary", + self.project_dir / "03_revision", + ] + + for dir_path in required_dirs: + if not dir_path.exists(): + logger.error(f"Writer: Expected directory missing: {dir_path}") + raise RuntimeError( + f"Project structure invalid: missing {dir_path.name} directory" + ) + + logger.info( + f"Writer: Project structure verified at {self.project_dir.absolute()}" + ) + +
+[docs] + def compile_manuscript( + self, + timeout: int = 300, + log_callback: Optional[Callable[[str], None]] = None, + progress_callback: Optional[Callable[[int, str], None]] = None, + ) -> CompilationResult: + """ + Compile manuscript to PDF with optional live callbacks. + + Runs scripts/shell/compile_manuscript.sh with configured settings. + + Parameters + ---------- + timeout : int, optional + Maximum compilation time in seconds (default: 300). + log_callback : callable, optional + Called with each log line: log_callback("Running pdflatex..."). + progress_callback : callable, optional + Called with progress: progress_callback(50, "Pass 2/3"). + + Returns + ------- + CompilationResult + With success status, PDF path, and errors/warnings. + + Examples + -------- + >>> writer = Writer(Path("my_paper")) + >>> result = writer.compile_manuscript() + >>> if result.success: + ... print(f"PDF created: {result.output_pdf}") + """ + return compile_manuscript( + self.project_dir, + timeout=timeout, + log_callback=log_callback, + progress_callback=progress_callback, + )
+ + +
+[docs] + def compile_supplementary( + self, + timeout: int = 300, + log_callback: Optional[Callable[[str], None]] = None, + progress_callback: Optional[Callable[[int, str], None]] = None, + ) -> CompilationResult: + """ + Compile supplementary materials to PDF with optional live callbacks. + + Runs scripts/shell/compile_supplementary.sh with configured settings. + + Parameters + ---------- + timeout : int, optional + Maximum compilation time in seconds (default: 300). + log_callback : callable, optional + Called with each log line. + progress_callback : callable, optional + Called with progress updates. + + Returns + ------- + CompilationResult + With success status, PDF path, and errors/warnings. + + Examples + -------- + >>> writer = Writer(Path("my_paper")) + >>> result = writer.compile_supplementary() + >>> if result.success: + ... print(f"PDF created: {result.output_pdf}") + """ + return compile_supplementary( + self.project_dir, + timeout=timeout, + log_callback=log_callback, + progress_callback=progress_callback, + )
+ + +
+[docs] + def compile_revision( + self, + track_changes: bool = False, + timeout: int = 300, + log_callback: Optional[Callable[[str], None]] = None, + progress_callback: Optional[Callable[[int, str], None]] = None, + ) -> CompilationResult: + """ + Compile revision document with optional change tracking and live callbacks. + + Runs scripts/shell/compile_revision.sh with configured settings. + + Parameters + ---------- + track_changes : bool, optional + Enable change tracking in compiled PDF (default: False). + timeout : int, optional + Maximum compilation time in seconds (default: 300). + log_callback : callable, optional + Called with each log line. + progress_callback : callable, optional + Called with progress updates. + + Returns + ------- + CompilationResult + With success status, PDF path, and errors/warnings. + + Examples + -------- + >>> writer = Writer(Path("my_paper")) + >>> result = writer.compile_revision(track_changes=True) + >>> if result.success: + ... print(f"Revision PDF: {result.output_pdf}") + """ + return compile_revision( + self.project_dir, + track_changes=track_changes, + timeout=timeout, + log_callback=log_callback, + progress_callback=progress_callback, + )
+ + +
+[docs] + def watch(self, on_compile: Optional[Callable] = None) -> None: + """Auto-recompile on file changes.""" + watch_manuscript(self.project_dir, on_compile=on_compile)
+ + +
+[docs] + def get_pdf(self, doc_type: str = "manuscript") -> Optional[Path]: + """Get output PDF path (Read).""" + pdf = self.project_dir / DOC_TYPE_DIRS[doc_type] / f"{doc_type}.pdf" + return pdf if pdf.exists() else None
+ + +
+[docs] + def delete(self) -> bool: + """Delete project directory (Delete).""" + try: + logger.warning( + f"Writer: Deleting project directory at {self.project_dir.absolute()}" + ) + shutil.rmtree(self.project_dir) + logger.info( + f"Writer: Successfully deleted project at {self.project_dir.absolute()}" + ) + return True + except Exception as e: + logger.error( + f"Writer: Failed to delete project directory at {self.project_dir.absolute()}: {e}" + ) + return False
+
+ + + +__all__ = ["Writer"] + +# EOF +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_sources/api/index.rst.txt b/src/scitex/_sphinx_html/_sources/api/index.rst.txt new file mode 100644 index 000000000..90a12130a --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/index.rst.txt @@ -0,0 +1,33 @@ +API Reference +============= + +Auto-generated API documentation for all SciTeX modules. + +.. toctree:: + :maxdepth: 1 + + scitex.session + scitex.io + scitex.config + scitex.logging + scitex.repro + scitex.clew + scitex.stats + scitex.plt + scitex.dsp + scitex.diagram + scitex.scholar + scitex.writer + scitex.linter + scitex.ai + scitex.nn + scitex.template + scitex.decorators + scitex.introspect + scitex.pd + scitex.gen + scitex.db + scitex.dict + scitex.str + scitex.path + scitex.social diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.ai.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.ai.rst.txt new file mode 100644 index 000000000..51d7c6612 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.ai.rst.txt @@ -0,0 +1,6 @@ +scitex.ai API Reference +======================= + +.. automodule:: scitex.ai + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.clew.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.clew.rst.txt new file mode 100644 index 000000000..ff3065d6e --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.clew.rst.txt @@ -0,0 +1,6 @@ +scitex.clew API Reference +========================= + +.. automodule:: scitex.clew + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.config.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.config.rst.txt new file mode 100644 index 000000000..4ae935a83 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.config.rst.txt @@ -0,0 +1,6 @@ +scitex.config API Reference +=========================== + +.. automodule:: scitex.config + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.db.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.db.rst.txt new file mode 100644 index 000000000..a4c64e9b9 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.db.rst.txt @@ -0,0 +1,6 @@ +scitex.db API Reference +======================= + +.. automodule:: scitex.db + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.decorators.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.decorators.rst.txt new file mode 100644 index 000000000..548081db2 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.decorators.rst.txt @@ -0,0 +1,6 @@ +scitex.decorators API Reference +=============================== + +.. automodule:: scitex.decorators + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.diagram.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.diagram.rst.txt new file mode 100644 index 000000000..ad3798cfa --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.diagram.rst.txt @@ -0,0 +1,6 @@ +scitex.diagram API Reference +============================ + +.. automodule:: scitex.diagram + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.dict.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.dict.rst.txt new file mode 100644 index 000000000..b5da0fa0c --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.dict.rst.txt @@ -0,0 +1,6 @@ +scitex.dict API Reference +========================= + +.. automodule:: scitex.dict + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.dsp.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.dsp.rst.txt new file mode 100644 index 000000000..fdabef2c7 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.dsp.rst.txt @@ -0,0 +1,6 @@ +scitex.dsp API Reference +======================== + +.. automodule:: scitex.dsp + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.gen.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.gen.rst.txt new file mode 100644 index 000000000..571f50ac0 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.gen.rst.txt @@ -0,0 +1,6 @@ +scitex.gen API Reference +======================== + +.. automodule:: scitex.gen + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.introspect.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.introspect.rst.txt new file mode 100644 index 000000000..b71918efc --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.introspect.rst.txt @@ -0,0 +1,6 @@ +scitex.introspect API Reference +=============================== + +.. automodule:: scitex.introspect + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.io.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.io.rst.txt new file mode 100644 index 000000000..a46a88560 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.io.rst.txt @@ -0,0 +1,6 @@ +scitex.io API Reference +======================= + +.. automodule:: scitex.io + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.linter.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.linter.rst.txt new file mode 100644 index 000000000..bd8389f2c --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.linter.rst.txt @@ -0,0 +1,6 @@ +scitex.linter +============= + +.. automodule:: scitex.linter + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.logging.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.logging.rst.txt new file mode 100644 index 000000000..c8a959ecf --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.logging.rst.txt @@ -0,0 +1,6 @@ +scitex.logging API Reference +============================ + +.. automodule:: scitex.logging + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.nn.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.nn.rst.txt new file mode 100644 index 000000000..04e12a48d --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.nn.rst.txt @@ -0,0 +1,6 @@ +scitex.nn API Reference +======================= + +.. automodule:: scitex.nn + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.path.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.path.rst.txt new file mode 100644 index 000000000..e42beebb5 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.path.rst.txt @@ -0,0 +1,6 @@ +scitex.path API Reference +========================= + +.. automodule:: scitex.path + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.pd.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.pd.rst.txt new file mode 100644 index 000000000..d8d37568e --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.pd.rst.txt @@ -0,0 +1,6 @@ +scitex.pd API Reference +======================= + +.. automodule:: scitex.pd + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.plt.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.plt.rst.txt new file mode 100644 index 000000000..61b6c8a21 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.plt.rst.txt @@ -0,0 +1,12 @@ +scitex.plt API Reference +======================== + +.. note:: + + ``scitex.plt`` wraps matplotlib with data-tracking axes. The plotting API + is migrating to `figrecipe `_. + See :doc:`/modules/plt` for usage documentation. + +.. automodule:: scitex.plt + :members: subplots + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.repro.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.repro.rst.txt new file mode 100644 index 000000000..e708e5279 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.repro.rst.txt @@ -0,0 +1,6 @@ +scitex.repro API Reference +========================== + +.. automodule:: scitex.repro + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.scholar.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.scholar.rst.txt new file mode 100644 index 000000000..a31146632 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.scholar.rst.txt @@ -0,0 +1,6 @@ +scitex.scholar API Reference +============================ + +.. automodule:: scitex.scholar + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.session.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.session.rst.txt new file mode 100644 index 000000000..ababb2fdd --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.session.rst.txt @@ -0,0 +1,8 @@ +scitex.session API Reference +============================ + +.. automodule:: scitex.session + :members: + :no-undoc-members: + :show-inheritance: + :exclude-members: _InjectedSentinel diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.social.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.social.rst.txt new file mode 100644 index 000000000..e6ba5dc39 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.social.rst.txt @@ -0,0 +1,6 @@ +scitex.social API Reference +=========================== + +.. automodule:: scitex.social + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.stats.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.stats.rst.txt new file mode 100644 index 000000000..c816a97be --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.stats.rst.txt @@ -0,0 +1,6 @@ +scitex.stats API Reference +========================== + +.. automodule:: scitex.stats + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.str.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.str.rst.txt new file mode 100644 index 000000000..436f10973 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.str.rst.txt @@ -0,0 +1,6 @@ +scitex.str API Reference +======================== + +.. automodule:: scitex.str + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.template.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.template.rst.txt new file mode 100644 index 000000000..be04bf7e3 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.template.rst.txt @@ -0,0 +1,6 @@ +scitex.template API Reference +============================= + +.. automodule:: scitex.template + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/api/scitex.writer.rst.txt b/src/scitex/_sphinx_html/_sources/api/scitex.writer.rst.txt new file mode 100644 index 000000000..d32944356 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/api/scitex.writer.rst.txt @@ -0,0 +1,6 @@ +scitex.writer +============= + +.. automodule:: scitex.writer + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/core_concepts.rst.txt b/src/scitex/_sphinx_html/_sources/core_concepts.rst.txt new file mode 100644 index 000000000..4484557c4 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/core_concepts.rst.txt @@ -0,0 +1,181 @@ +Core Concepts +============= + +Research Workflow +----------------- + +SciTeX covers the full research pipeline, from literature review to publication: + +.. image:: _static/workflow.png + :width: 100% + :alt: Research workflow: Question → Literature → Data → Analysis → Figures → Manuscript → Verify → Publication + +Each stage maps to a SciTeX module. The ``@stx.session`` decorator ties them +together, ensuring every step is reproducible and provenance-tracked. + +Architecture +------------ + +SciTeX is organized around a simple principle: every research script should be +a reproducible, self-documenting unit of work. The ``@stx.session`` decorator +enforces this by managing outputs, logging, and configuration automatically. + +.. image:: _static/architecture.png + :width: 100% + :alt: Module architecture: Experiment (session, config, io, logging, repro), Analysis & Visualization (stats, dsp, plt, diagram), Publication (scholar, writer, clew) + +Modules are grouped into three layers -- **Experiment** infrastructure (blue), +**Analysis & Visualization** tools (orange), and **Publication** (purple). +See :doc:`modules/index` for the full module reference. + +The Session Model +----------------- + +``@stx.session`` is the core abstraction. It wraps a function and provides: + +1. **Output directory**: ``script_out/FINISHED_SUCCESS//`` +2. **Logging**: All stdout/stderr captured to ``script.log`` +3. **Config injection**: YAML files from ``./config/`` merged and injected +4. **CLI generation**: Function parameters become ``--flags`` +5. **Provenance**: File hashes recorded to SQLite for reproducibility + +.. code-block:: python + + import scitex as stx + + @stx.session + def main( + lr=0.001, # --lr 0.01 + epochs=100, # --epochs 50 + CONFIG=stx.INJECTED, # from ./config/*.yaml + plt=stx.INJECTED, # pre-configured matplotlib + logger=stx.INJECTED, # session logger + ): + """Train a model. Docstring becomes --help text.""" + logger.info(f"Training with lr={lr}, epochs={epochs}") + + # stx.io.save paths are relative to session output dir + stx.io.save({"lr": lr, "epochs": epochs}, "params.yaml") + + return 0 + +Session output tree: + +.. code-block:: text + + train_out/ + ├── RUNNING/ # while script runs + │ └── 20260213_143022_AB12/ + │ ├── params.yaml + │ └── train.log + └── FINISHED_SUCCESS/ # after successful exit + └── 20260213_143022_AB12/ # moved here on completion + ├── params.yaml + └── train.log + +Universal I/O +------------- + +``stx.io.save`` and ``stx.io.load`` dispatch on file extension: + +.. list-table:: + :header-rows: 1 + + * - Extension + - Data Type + - Backend + * - ``.csv`` + - DataFrame + - pandas + * - ``.npy``, ``.npz`` + - ndarray + - numpy + * - ``.pkl``, ``.pickle`` + - any object + - pickle + * - ``.yaml``, ``.yml`` + - dict + - PyYAML + * - ``.json`` + - dict/list + - json + * - ``.png``, ``.jpg``, ``.svg``, ``.pdf`` + - Figure + - matplotlib + * - ``.hdf5``, ``.h5`` + - dict/array + - h5py + * - ``.mat`` + - dict + - scipy.io + * - ``.pth``, ``.pt`` + - state_dict + - torch + * - ``.parquet`` + - DataFrame + - pyarrow + +When saving a matplotlib Figure, SciTeX also exports: + +- A ``.csv`` with the plotted data (extracted from axes) +- A ``.yaml`` recipe for reproducing the figure + +Provenance Tracking (Clew) +-------------------------- + +Inside ``@stx.session``, every ``stx.io.save`` and ``stx.io.load`` call +records the file's SHA-256 hash to a local SQLite database. This enables: + +- **Verification**: Check if output files have been modified since creation +- **DAG reconstruction**: Trace which inputs produced which outputs +- **Cross-session linking**: If script B loads a file that script A produced, + the parent-child relationship is recorded automatically + +.. code-block:: bash + + # Check verification status + scitex clew status + + # Verify a specific session + scitex clew run + + # Generate a dependency diagram + scitex clew mermaid + +Configuration +------------- + +SciTeX uses a priority-based config system: + +1. **CLI flags** (highest priority): ``--lr 0.01`` +2. **Config files**: ``./config/*.yaml`` (merged alphabetically) +3. **Function defaults** (lowest priority): ``lr=0.001`` + +.. code-block:: yaml + + # config/experiment.yaml + DATA_DIR: ./data + MODEL: + hidden_size: 256 + dropout: 0.1 + + # config/PATH.yaml + OUTPUT_DIR: ${HOME}/results # env var substitution + +Access in code: + +.. code-block:: python + + @stx.session + def main(CONFIG=stx.INJECTED, **kw): + data_dir = CONFIG["DATA_DIR"] + hidden = CONFIG["MODEL"]["hidden_size"] + +Best Practices +-------------- + +1. **One session per script**: Each ``.py`` file should have one ``@stx.session`` function +2. **Use relative paths in save/load**: They resolve relative to the session output directory +3. **Return 0 for success**: The exit code determines the output directory name (``FINISHED_SUCCESS`` vs ``FINISHED_ERROR``) +4. **Put config in** ``./config/*.yaml``: Keeps parameters separate from code +5. **Use** ``stx.repro.fix_seeds(42)`` **for determinism**: Fixes numpy, torch, random seeds in one call diff --git a/src/scitex/_sphinx_html/_sources/gallery.rst.txt b/src/scitex/_sphinx_html/_sources/gallery.rst.txt new file mode 100644 index 000000000..9afe7c776 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/gallery.rst.txt @@ -0,0 +1,206 @@ +Plot Gallery +============ + +``stx.plt`` wraps matplotlib axes with data-tracking methods. +All ``plot_*`` methods record data for automatic CSV export. + +Generate the full gallery locally: + +.. code-block:: python + + import scitex as stx + stx.plt.gallery.generate(output_dir="./gallery") + +Line Plots +---------- + +.. list-table:: + :widths: 50 50 + + * - .. figure:: _static/gallery/line/stx_line.png + :width: 100% + + ``ax.plot_line(x, y)`` + + - .. figure:: _static/gallery/line/stx_shaded_line.png + :width: 100% + + ``ax.plot_shaded_line(x, y_mean, y_std)`` + + * - .. figure:: _static/gallery/line/plot.png + :width: 100% + + ``ax.plot(x, y)`` (standard matplotlib) + + - .. figure:: _static/gallery/line/step.png + :width: 100% + + ``ax.step(x, y)`` + +Statistical Plots +----------------- + +.. list-table:: + :widths: 50 50 + + * - .. figure:: _static/gallery/statistical/stx_mean_std.png + :width: 100% + + ``ax.plot_mean_std(groups)`` + + - .. figure:: _static/gallery/statistical/stx_mean_ci.png + :width: 100% + + ``ax.plot_mean_ci(groups)`` + + * - .. figure:: _static/gallery/statistical/stx_median_iqr.png + :width: 100% + + ``ax.plot_median_iqr(groups)`` + + - .. figure:: _static/gallery/statistical/stx_errorbar.png + :width: 100% + + ``ax.plot_errorbar(x, y, yerr)`` + +Distribution Plots +------------------ + +.. list-table:: + :widths: 50 50 + + * - .. figure:: _static/gallery/distribution/stx_kde.png + :width: 100% + + ``ax.plot_kde(data)`` + + - .. figure:: _static/gallery/distribution/stx_ecdf.png + :width: 100% + + ``ax.plot_ecdf(data)`` + + * - .. figure:: _static/gallery/distribution/stx_joyplot.png + :width: 100% + + ``ax.plot_joyplot(groups)`` + + - .. figure:: _static/gallery/distribution/hist.png + :width: 100% + + ``ax.hist(data)`` + +Categorical Plots +----------------- + +.. list-table:: + :widths: 50 50 + + * - .. figure:: _static/gallery/categorical/stx_violin.png + :width: 100% + + ``ax.plot_violin(groups)`` + + - .. figure:: _static/gallery/categorical/stx_box.png + :width: 100% + + ``ax.plot_box(groups)`` + + * - .. figure:: _static/gallery/categorical/stx_bar.png + :width: 100% + + ``ax.plot_bar(labels, values)`` + + - .. figure:: _static/gallery/categorical/stx_barh.png + :width: 100% + + ``ax.plot_barh(labels, values)`` + +Scatter Plots +------------- + +.. list-table:: + :widths: 50 50 + + * - .. figure:: _static/gallery/scatter/stx_scatter.png + :width: 100% + + ``ax.plot_scatter(x, y)`` + + - .. figure:: _static/gallery/scatter/scatter.png + :width: 100% + + ``ax.scatter(x, y)`` (standard matplotlib) + +Heatmaps and Grids +------------------- + +.. list-table:: + :widths: 50 50 + + * - .. figure:: _static/gallery/grid/stx_heatmap.png + :width: 100% + + ``ax.plot_heatmap(matrix)`` + + - .. figure:: _static/gallery/grid/stx_conf_mat.png + :width: 100% + + ``ax.plot_conf_mat(cm)`` + + * - .. figure:: _static/gallery/grid/stx_image.png + :width: 100% + + ``ax.plot_image(img)`` + + - .. figure:: _static/gallery/grid/stx_imshow.png + :width: 100% + + ``ax.plot_imshow(data)`` + +Area Plots +---------- + +.. list-table:: + :widths: 50 50 + + * - .. figure:: _static/gallery/area/stx_fill_between.png + :width: 100% + + ``ax.plot_fill_between(x, y1, y2)`` + + - .. figure:: _static/gallery/area/stx_fillv.png + :width: 100% + + ``ax.plot_fillv(x_start, x_end)`` + +Special Plots +------------- + +.. list-table:: + :widths: 50 50 + + * - .. figure:: _static/gallery/special/stx_raster.png + :width: 100% + + ``ax.plot_raster(spike_times)`` + + - .. figure:: _static/gallery/special/stx_rectangle.png + :width: 100% + + ``ax.plot_rectangle(xy, w, h)`` + +Contour and Vector Plots +------------------------- + +.. list-table:: + :widths: 50 50 + + * - .. figure:: _static/gallery/contour/stx_contour.png + :width: 100% + + ``ax.plot_contour(x, y, z)`` + + - .. figure:: _static/gallery/vector/quiver.png + :width: 100% + + ``ax.quiver(x, y, u, v)`` diff --git a/src/scitex/_sphinx_html/_sources/index.rst.txt b/src/scitex/_sphinx_html/_sources/index.rst.txt new file mode 100644 index 000000000..e8dcdd7b0 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/index.rst.txt @@ -0,0 +1,78 @@ +.. SciTeX documentation master file + +SciTeX -- Modular Python Toolkit for Researchers and AI Agents +============================================================= + +A Python framework for reproducible scientific research. + +.. image:: _static/workflow.png + :alt: SciTeX Ecosystem + :align: center + :width: 800px + +Role in SciTeX Ecosystem +------------------------ + +``scitex`` is the **unified orchestrator** package. It re-exports from sub-packages +so users have a single import (``import scitex``). It does not contain runtime logic +itself -- it delegates to sub-packages. + +.. code-block:: text + + scitex (this package) -- orchestrator, templates, CLI, MCP server + |-- scitex.app <- scitex-app (runtime SDK: file I/O, config, validation) + |-- scitex.ui <- scitex-ui (React/TS components: workspace, data-table) + +-- scitex.plt <- figrecipe (figures: plotting, diagrams, recipes) + +- **scitex-app** (`docs `_): Runtime SDK that apps import at execution time +- **scitex-ui** (`docs `_): Shared React/TypeScript component library +- **figrecipe** (`docs `_): Reference app -- figures, diagrams, recipes + +Four Freedoms for Research +-------------------------- + +0. The freedom to **run** your research anywhere -- your machine, your terms. +1. The freedom to **study** how every step works -- from raw data to final manuscript. +2. The freedom to **redistribute** your workflows, not just your papers. +3. The freedom to **modify** any module and share improvements with the community. + +AGPL-3.0 -- because research infrastructure deserves the same freedoms as the software it runs on. + +.. code-block:: python + + import scitex as stx + + @stx.session + def main(n_samples=100, plt=stx.INJECTED): + import numpy as np + x = np.linspace(0, 2 * np.pi, n_samples) + y = np.sin(x) + np.random.normal(0, 0.1, n_samples) + + fig, ax = plt.subplots() + ax.plot_line(x, y) + stx.io.save(fig, "sine.png") # PNG + CSV + YAML recipe + return 0 + +- ``@stx.session`` -- Reproducible runs with CLI, config, and provenance tracking +- ``stx.io`` -- Save/load 30+ formats through a single interface +- ``stx.plt`` -- Publication figures with auto CSV data export +- ``stx.stats`` -- 23 statistical tests with effect sizes and CI +- ``stx.scholar`` -- Search, download, and enrich papers +- 120+ MCP tools for AI-assisted research workflows + +.. toctree:: + :maxdepth: 2 + :caption: Getting Started + + modules/index + installation + quickstart + core_concepts + gallery + +.. toctree:: + :maxdepth: 1 + :caption: API Reference + :hidden: + + api/index diff --git a/src/scitex/_sphinx_html/_sources/installation.rst.txt b/src/scitex/_sphinx_html/_sources/installation.rst.txt new file mode 100644 index 000000000..5b692ce9f --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/installation.rst.txt @@ -0,0 +1,166 @@ +Installation +============ + +Requirements +------------ + +- **Python 3.10+** +- ``uv`` (strongly recommended) — install with ``pip install uv`` or + ``curl -LsSf https://astral.sh/uv/install.sh | sh`` +- ``pip`` 21+ also works, but expect 30–90 min for ``scitex[all]`` + (see warning below) + + +.. warning:: + + ``pip install "scitex[all]"`` typically takes **30–90 minutes** because + pip's serial resolver walks version histories of the large transitive + dependency set (numpy/pandas/torch/jax/playwright/openalex-local/…). + Use **uv** instead — it resolves the same set in parallel in **1–3 + minutes**. Every ``pip install`` line on this page also works as + ``uv pip install`` and we recommend the uv form. + + +Core Install +------------ + +The base package pulls in only lightweight dependencies and gives you access +to session management, path utilities, string helpers, and the module +discovery system. + +.. code-block:: bash + + uv pip install scitex # recommended + pip install scitex # also works, slower for [all] + + +Recommended: Install Everything +------------------------------- + +If you want the full experience (plotting, statistics, I/O, scholar, writer, +and every other module): + +.. code-block:: bash + + uv pip install "scitex[all]" # ~3 min + pip install "scitex[all]" # ~30–90 min (resolver thrash) + + +Research Workflow +----------------- + +For a typical research project you need figures, statistics, and literature +search but not audio, browser automation, or cloud tools: + +.. code-block:: bash + + pip install "scitex[plt,stats,scholar]" + + +Per-Module Extras +----------------- + +Install only what you need. Each extra maps to a self-contained capability. + +.. list-table:: + :header-rows: 1 + :widths: 15 55 + + * - Extra + - Description + * - ``plt`` + - Publication-ready figures via figrecipe (matplotlib, seaborn, Pillow) + * - ``stats`` + - Hypothesis testing, effect sizes, power analysis (scitex-stats, scipy, statsmodels) + * - ``io`` + - Unified I/O for 40+ formats (HDF5, Excel, YAML, PDF, images, ...) + * - ``scholar`` + - Literature search and PDF management (CrossRef, OpenAlex, Semantic Scholar) + * - ``writer`` + - LaTeX manuscript compilation, BibTeX management, Overleaf export + * - ``audio`` + - Text-to-speech and audio utilities (scitex-audio) + * - ``ai`` + - LLM APIs (OpenAI, Anthropic, Google, Groq) and ML tools (scikit-learn) + * - ``browser`` + - Web automation via Playwright + * - ``capture`` + - Screenshot capture (mss, Playwright) + * - ``dataset`` + - Scientific dataset access (DANDI, OpenNeuro, PhysioNet) + * - ``cloud`` + - Cloud integration utilities + * - ``app`` + - Unified file storage SDK (scitex-app) + * - ``session`` + - Session decorator with reproducibility logging + * - ``diagram`` + - Diagram generation (Mermaid, Graphviz) + * - ``db`` + - Database access (SQLAlchemy, PostgreSQL) + * - ``cv`` + - Computer vision (OpenCV, Pillow) + * - ``dsp`` + - Digital signal processing (scipy, tensorpac) + * - ``social`` + - Social media posting (socialia) + * - ``tunnel`` + - SSH tunnel management (scitex-tunnel) + * - ``all`` + - Everything above + + +Example combinations: + +.. code-block:: bash + + # Neuroscience analysis + pip install "scitex[plt,stats,dsp,dataset]" + + # Paper writing + pip install "scitex[writer,scholar,plt]" + + # AI agent development + pip install "scitex[ai,browser,capture]" + + +Development Install +------------------- + +Clone the repository and install in editable mode with development tools: + +.. code-block:: bash + + git clone https://github.com/ywatanabe1989/scitex-python.git + cd scitex-python + pip install -e ".[dev]" + +The ``dev`` extra includes pytest, ruff, mypy, Sphinx, and build tools. + +Using uv (recommended) +^^^^^^^^^^^^^^^^^^^^^^^ + +`uv `_ resolves and installs dependencies +in parallel from a Rust resolver — roughly 10–30× faster than pip on +``scitex[all]`` (3 min vs. 30–90 min). Install it once with +``pip install uv`` (or the standalone shell installer), then prefix +every install command on this page with ``uv``: + +.. code-block:: bash + + uv pip install -e ".[dev]" + + +Verifying the Installation +-------------------------- + +.. code-block:: python + + import scitex as stx + print(stx.__version__) + +To check which optional modules are available: + +.. code-block:: python + + stx.usage.list() diff --git a/src/scitex/_sphinx_html/_sources/modules/ai.rst.txt b/src/scitex/_sphinx_html/_sources/modules/ai.rst.txt new file mode 100644 index 000000000..b1f49f0c2 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/ai.rst.txt @@ -0,0 +1,81 @@ +AI Module (``stx.ai``) +====================== + +Machine learning utilities for training, classification, and metrics +with PyTorch and scikit-learn. + +Quick Reference +--------------- + +.. code-block:: python + + import scitex as stx + + # Training utilities + from scitex.ai import LearningCurveLogger, EarlyStopping + + logger = LearningCurveLogger() + stopper = EarlyStopping(patience=10, direction="minimize") + + for epoch in range(100): + # ... training loop ... + logger({"loss": loss, "acc": acc}, step="Training") + if stopper(val_loss, {"model": model_path}, epoch): + break + + logger.plot_learning_curves(spath="curves.png") + + # Classification + from scitex.ai import ClassificationReporter, Classifier + + clf = Classifier()("SVC") + reporter = ClassificationReporter(output_dir="./results") + reporter.calculate_metrics(y_true, y_pred, y_proba) + reporter.save_summary() + +Training +-------- + +- ``LearningCurveLogger`` -- Track and visualize training/validation/test metrics across epochs +- ``EarlyStopping`` -- Monitor validation metrics and stop when improvement plateaus + +Classification +-------------- + +- ``ClassificationReporter`` -- Unified reporter for single/multi-task classification (balanced accuracy, MCC, ROC-AUC, confusion matrices) +- ``Classifier`` -- Factory for scikit-learn classifiers (SVC, KNN, Logistic Regression, AdaBoost, ...) +- ``CrossValidationExperiment`` -- Cross-validation framework + +Metrics +------- + +Standardized ``calc_*`` functions: + +- ``calc_bacc`` -- Balanced accuracy +- ``calc_mcc`` -- Matthews Correlation Coefficient +- ``calc_conf_mat`` -- Confusion matrix +- ``calc_roc_auc`` -- ROC-AUC score +- ``calc_pre_rec_auc`` -- Precision-Recall AUC +- ``calc_feature_importance`` -- Feature importance scores + +Visualization +------------- + +- ``plot_learning_curve`` -- Training/validation curves +- ``stx_conf_mat`` -- Confusion matrix heatmap +- ``plot_roc_curve`` -- ROC curve +- ``plot_pre_rec_curve`` -- Precision-Recall curve +- ``plot_feature_importance`` -- Feature importance bar plots + +Other +----- + +- ``MultiTaskLoss`` -- Multi-task learning loss weighting +- ``get_optimizer`` / ``set_optimizer`` -- Optimizer management +- ``GenAI`` -- Generative AI wrapper (lazy-loaded) +- Clustering: ``pca``, ``umap`` + +API Reference +------------- + +See :doc:`/api/scitex.ai` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/app.rst.txt b/src/scitex/_sphinx_html/_sources/modules/app.rst.txt new file mode 100644 index 000000000..4b03e4a06 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/app.rst.txt @@ -0,0 +1,4 @@ +app Module (``stx.app``) +======================== + +See :doc:`/api/scitex.app` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/audio.rst.txt b/src/scitex/_sphinx_html/_sources/modules/audio.rst.txt new file mode 100644 index 000000000..eaf9823f8 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/audio.rst.txt @@ -0,0 +1,4 @@ +audio Module (``stx.audio``) +============================ + +See :doc:`/api/scitex.audio` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/audit.rst.txt b/src/scitex/_sphinx_html/_sources/modules/audit.rst.txt new file mode 100644 index 000000000..ca8e0bcdc --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/audit.rst.txt @@ -0,0 +1,4 @@ +audit Module (``stx.audit``) +============================ + +See :doc:`/api/scitex.audit` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/benchmark.rst.txt b/src/scitex/_sphinx_html/_sources/modules/benchmark.rst.txt new file mode 100644 index 000000000..10c989b1e --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/benchmark.rst.txt @@ -0,0 +1,4 @@ +benchmark Module (``stx.benchmark``) +==================================== + +See :doc:`/api/scitex.benchmark` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/bridge.rst.txt b/src/scitex/_sphinx_html/_sources/modules/bridge.rst.txt new file mode 100644 index 000000000..12fe2d373 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/bridge.rst.txt @@ -0,0 +1,4 @@ +bridge Module (``stx.bridge``) +============================== + +See :doc:`/api/scitex.bridge` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/browser.rst.txt b/src/scitex/_sphinx_html/_sources/modules/browser.rst.txt new file mode 100644 index 000000000..d8ab6222e --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/browser.rst.txt @@ -0,0 +1,4 @@ +browser Module (``stx.browser``) +================================ + +See :doc:`/api/scitex.browser` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/canvas.rst.txt b/src/scitex/_sphinx_html/_sources/modules/canvas.rst.txt new file mode 100644 index 000000000..bd92b96bc --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/canvas.rst.txt @@ -0,0 +1,4 @@ +canvas Module (``stx.canvas``) +============================== + +See :doc:`/api/scitex.canvas` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/capture.rst.txt b/src/scitex/_sphinx_html/_sources/modules/capture.rst.txt new file mode 100644 index 000000000..ced769f55 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/capture.rst.txt @@ -0,0 +1,4 @@ +capture Module (``stx.capture``) +================================ + +See :doc:`/api/scitex.capture` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/clew.rst.txt b/src/scitex/_sphinx_html/_sources/modules/clew.rst.txt new file mode 100644 index 000000000..8dbe05f85 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/clew.rst.txt @@ -0,0 +1,93 @@ +Clew Module (``stx.clew``) +========================== + +Hash-based provenance tracking for reproducible science. Clew (Ariadne's +thread) records file hashes during ``@stx.session`` runs and traces +dependency chains back to source. + +How It Works +------------ + +1. ``@stx.session`` starts a tracking session +2. ``stx.io.load()`` records input file hashes +3. ``stx.io.save()`` records output file hashes +4. Session close computes a combined hash of all inputs/outputs +5. Later, ``stx.clew`` can verify nothing has changed + +.. code-block:: python + + import scitex as stx + + # Automatic -- just use @stx.session + stx.io + @stx.session + def main(): + data = stx.io.load("input.csv") # Tracked as input + result = process(data) + stx.io.save(result, "output.png") # Tracked as output + return 0 + + # Verify later + stx.clew.status() # Like git status + stx.clew.run("session_id") # Verify by hash + stx.clew.chain("output.png") # Trace to source + +CLI Commands +------------ + +.. code-block:: bash + + scitex clew status # Show changed files + scitex clew list # List all tracked runs + scitex clew run # Verify a specific run + scitex clew chain # Trace dependency chain + scitex clew stats # Database statistics + +Verification Levels +------------------- + +- **CACHE** -- Hash comparison only (fast). Checks if files match stored hashes. +- **RERUN** -- Re-execute scripts and compare outputs (thorough). Catches logic errors. + +.. code-block:: python + + # Fast: hash comparison + result = stx.clew.run("session_id") + + # Thorough: re-execute and compare + result = stx.clew.run("session_id", from_scratch=True) + +Dependency Chains +----------------- + +Clew traces ``parent_session`` links to build a DAG from final output +back to original source: + +.. code-block:: python + + chain = stx.clew.chain("final_figure.png") + # Shows: source.py → intermediate.csv → analysis.py → final_figure.png + + # Visualize as Mermaid DAG + stx.clew.mermaid("session_id") + +Verification Statuses +--------------------- + +- ``VERIFIED`` -- Files match expected hashes +- ``MISMATCH`` -- Files differ from stored hashes +- ``MISSING`` -- Files no longer exist +- ``UNKNOWN`` -- No prior tracking data + +Key Functions +------------- + +- ``status()`` -- Show changed items (like ``git status``) +- ``run(session_id)`` -- Verify a specific run +- ``chain(target_file)`` -- Trace dependency chain +- ``list_runs(limit, status)`` -- List tracked runs +- ``stats()`` -- Database statistics + +API Reference +------------- + +See :doc:`/api/scitex.clew` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/cli.rst.txt b/src/scitex/_sphinx_html/_sources/modules/cli.rst.txt new file mode 100644 index 000000000..c785b8f32 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/cli.rst.txt @@ -0,0 +1,4 @@ +cli Module (``stx.cli``) +======================== + +See :doc:`/api/scitex.cli` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/cloud.rst.txt b/src/scitex/_sphinx_html/_sources/modules/cloud.rst.txt new file mode 100644 index 000000000..f6b08e85f --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/cloud.rst.txt @@ -0,0 +1,4 @@ +cloud Module (``stx.cloud``) +============================ + +See :doc:`/api/scitex.cloud` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/compat.rst.txt b/src/scitex/_sphinx_html/_sources/modules/compat.rst.txt new file mode 100644 index 000000000..7977c33bc --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/compat.rst.txt @@ -0,0 +1,4 @@ +compat Module (``stx.compat``) +============================== + +See :doc:`/api/scitex.compat` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/config.rst.txt b/src/scitex/_sphinx_html/_sources/modules/config.rst.txt new file mode 100644 index 000000000..dbdcb26ac --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/config.rst.txt @@ -0,0 +1,87 @@ +Config Module (``stx.config``) +=============================== + +YAML-based configuration with priority resolution and path management. + +Priority Resolution +------------------- + +All config values follow the same precedence: + +.. code-block:: text + + direct argument > config/YAML > environment variable > default + +Quick Reference +--------------- + +.. code-block:: python + + import scitex as stx + + # Get global config (reads ~/.scitex/config.yaml) + config = stx.config.get_config() + + # Resolve with precedence + log_level = config.resolve("logging.level", default="INFO") + # Checks: direct → YAML → SCITEX_LOGGING_LEVEL env → "INFO" + + # Access nested values + config.get_nested("scholar", "crossref_email") + + # Path management + paths = stx.config.get_paths() + paths.scholar # ~/.scitex/scholar + paths.cache # ~/.scitex/cache + +YAML Configuration +------------------ + +.. code-block:: yaml + + # ~/.scitex/config.yaml (or ./config/*.yaml for projects) + logging: + level: INFO + + scholar: + crossref_email: ${CROSSREF_EMAIL:-user@example.com} + +Environment variable substitution with ``${VAR:-default}`` syntax is +supported in YAML files. + +Key Classes +----------- + +- ``ScitexConfig`` -- YAML-based config with env var substitution and precedence resolution +- ``PriorityConfig`` -- Dict-based config resolver for programmatic use +- ``ScitexPaths`` -- Centralized path manager (all paths derive from ``~/.scitex/``) +- ``EnvVar`` -- Environment variable definition dataclass + +Path Management +--------------- + +``ScitexPaths`` manages all SciTeX directories: + +.. code-block:: python + + paths = stx.config.get_paths() + paths.base # ~/.scitex (SCITEX_DIR) + paths.logs # ~/.scitex/logs + paths.cache # ~/.scitex/cache + paths.scholar # ~/.scitex/scholar + paths.rng # ~/.scitex/rng + paths.capture # ~/.scitex/capture + +Utility Functions +----------------- + +- ``get_config(path)`` -- Get singleton ScitexConfig instance +- ``get_paths(base_dir)`` -- Get singleton ScitexPaths instance +- ``load_yaml(path)`` -- Load YAML with env var substitution +- ``load_dotenv(path)`` -- Load ``.env`` file +- ``get_scitex_dir()`` -- Get SCITEX_DIR with precedence + +API Reference +------------- + +See :doc:`/api/scitex.config` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/container.rst.txt b/src/scitex/_sphinx_html/_sources/modules/container.rst.txt new file mode 100644 index 000000000..60631f757 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/container.rst.txt @@ -0,0 +1,4 @@ +container Module (``stx.container``) +==================================== + +See :doc:`/api/scitex.container` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/context.rst.txt b/src/scitex/_sphinx_html/_sources/modules/context.rst.txt new file mode 100644 index 000000000..fe6009e47 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/context.rst.txt @@ -0,0 +1,4 @@ +context Module (``stx.context``) +================================ + +See :doc:`/api/scitex.context` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/cv.rst.txt b/src/scitex/_sphinx_html/_sources/modules/cv.rst.txt new file mode 100644 index 000000000..3de4145dd --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/cv.rst.txt @@ -0,0 +1,4 @@ +cv Module (``stx.cv``) +====================== + +See :doc:`/api/scitex.cv` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/dataset.rst.txt b/src/scitex/_sphinx_html/_sources/modules/dataset.rst.txt new file mode 100644 index 000000000..35b8c8c4c --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/dataset.rst.txt @@ -0,0 +1,4 @@ +dataset Module (``stx.dataset``) +================================ + +See :doc:`/api/scitex.dataset` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/datetime.rst.txt b/src/scitex/_sphinx_html/_sources/modules/datetime.rst.txt new file mode 100644 index 000000000..915d124e1 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/datetime.rst.txt @@ -0,0 +1,4 @@ +datetime Module (``stx.datetime``) +================================== + +See :doc:`/api/scitex.datetime` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/db.rst.txt b/src/scitex/_sphinx_html/_sources/modules/db.rst.txt new file mode 100644 index 000000000..c961228cb --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/db.rst.txt @@ -0,0 +1,4 @@ +db Module (``stx.db``) +====================== + +See :doc:`/api/scitex.db` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/decorators.rst.txt b/src/scitex/_sphinx_html/_sources/modules/decorators.rst.txt new file mode 100644 index 000000000..960b6ad6d --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/decorators.rst.txt @@ -0,0 +1,79 @@ +Decorators Module (``stx.decorators``) +======================================= + +Function decorators for type conversion, batching, caching, and more. + +Quick Reference +--------------- + +.. code-block:: python + + from scitex.decorators import torch_fn, batch_fn, cache_disk, timeout + + @torch_fn + def process(x): + """Auto-converts inputs to torch tensors, outputs back.""" + return x * 2 + + @batch_fn + def predict(data, batch_size=32): + """Process data in batches with progress bar.""" + return model(data) + + @cache_disk + def expensive_computation(params): + """Results cached to disk for reuse.""" + return heavy_compute(params) + + @timeout(seconds=30) + def risky_call(): + """Raises TimeoutError if exceeds 30s.""" + return external_api() + +Type Conversion +--------------- + +Auto-convert between data frameworks: + +- ``@torch_fn`` -- Inputs to PyTorch tensors, outputs back to original type +- ``@numpy_fn`` -- Inputs to NumPy arrays +- ``@pandas_fn`` -- Inputs to pandas objects +- ``@xarray_fn`` -- Inputs to xarray objects + +Batch Processing +---------------- + +- ``@batch_fn`` -- Split input into batches with tqdm progress +- ``@batch_numpy_fn`` -- NumPy conversion + batching +- ``@batch_torch_fn`` -- PyTorch conversion + batching +- ``@batch_pandas_fn`` -- Pandas conversion + batching + +Caching +------- + +- ``@cache_mem`` -- In-memory function result caching +- ``@cache_disk`` -- Persistent disk-based caching +- ``@cache_disk_async`` -- Async disk caching + +Utilities +--------- + +- ``@deprecated(reason, forward_to)`` -- Mark functions as deprecated +- ``@not_implemented`` -- Mark as not yet implemented +- ``@timeout(seconds)`` -- Enforce execution time limits +- ``@preserve_doc`` -- Preserve docstrings when wrapping + +Auto-Ordering +------------- + +.. code-block:: python + + from scitex.decorators import enable_auto_order + + enable_auto_order() + # Now decorators are applied in optimal order regardless of code order + +API Reference +------------- + +See :doc:`/api/scitex.decorators` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/dev.rst.txt b/src/scitex/_sphinx_html/_sources/modules/dev.rst.txt new file mode 100644 index 000000000..14ab092d1 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/dev.rst.txt @@ -0,0 +1,4 @@ +dev Module (``stx.dev``) +======================== + +See :doc:`/api/scitex.dev` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/diagram.rst.txt b/src/scitex/_sphinx_html/_sources/modules/diagram.rst.txt new file mode 100644 index 000000000..b1d05f528 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/diagram.rst.txt @@ -0,0 +1,98 @@ +Diagram Module (``stx.diagram``) +================================= + +Paper-optimized diagram generation with Mermaid and Graphviz backends. + +Quick Reference +--------------- + +.. code-block:: python + + from scitex.diagram import Diagram + + # Create from Python + d = Diagram(type="workflow", title="Analysis Pipeline") + d.add_node("load", "Load Data", shape="stadium") + d.add_node("proc", "Process", shape="rounded", emphasis="primary") + d.add_node("save", "Save Results", shape="stadium", emphasis="success") + d.add_edge("load", "proc") + d.add_edge("proc", "save") + + # Export + d.to_mermaid("pipeline.mmd") + d.to_graphviz("pipeline.dot") + + # Or from YAML specification + d = Diagram.from_yaml("pipeline.diagram.yaml") + +YAML Specification +------------------ + +.. code-block:: yaml + + type: workflow + title: SciTeX Analysis Pipeline + + paper: + column: single # single or double + mode: publication # draft or publication + reading_direction: left_to_right + + layout: + groups: + Input: [load, preprocess] + Analysis: [analyze, test] + + nodes: + - id: load + label: Load Data + shape: stadium + - id: analyze + label: Statistical Test + shape: rounded + emphasis: primary + + edges: + - from: load + to: analyze + +Node Shapes +------------ + +``box``, ``rounded``, ``stadium``, ``diamond``, ``circle``, ``codeblock`` + +Node Emphasis +------------- + +- ``normal`` -- Default (dark) +- ``primary`` -- Key nodes (blue) +- ``success`` -- Positive outcomes (green) +- ``warning`` -- Issues (red) +- ``muted`` -- Secondary (gray) + +Diagram Types +------------- + +- ``workflow`` -- Left-to-right sequential processes +- ``decision`` -- Top-to-bottom decision trees +- ``pipeline`` -- Data pipeline stages +- ``hierarchy`` -- Tree structures +- ``comparison`` -- Side-by-side comparison + +Paper Modes +----------- + +- **draft** -- Full arrows, medium spacing, all edges visible +- **publication** -- Tight spacing, return edges invisible, optimized for column width + +Backends +-------- + +- **Mermaid** (``.mmd``) -- Web/markdown rendering +- **Graphviz DOT** (``.dot``) -- Tighter layouts, render with ``dot -Tpng`` +- **YAML** (``.diagram.yaml``) -- Semantic spec, human/LLM-readable + +API Reference +------------- + +See :doc:`/api/scitex.diagram` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/dict.rst.txt b/src/scitex/_sphinx_html/_sources/modules/dict.rst.txt new file mode 100644 index 000000000..156005490 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/dict.rst.txt @@ -0,0 +1,4 @@ +dict Module (``stx.dict``) +========================== + +See :doc:`/api/scitex.dict` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/dsp.rst.txt b/src/scitex/_sphinx_html/_sources/modules/dsp.rst.txt new file mode 100644 index 000000000..fc502bd2b --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/dsp.rst.txt @@ -0,0 +1,74 @@ +DSP Module (``stx.dsp``) +======================== + +Digital signal processing tools for filtering, spectral analysis, +and time-frequency decomposition. + +Quick Reference +--------------- + +.. code-block:: python + + import scitex as stx + import numpy as np + + # Generate test signal + fs = 1000 # Hz + t = np.arange(0, 1, 1/fs) + signal = np.sin(2 * np.pi * 10 * t) + 0.5 * np.sin(2 * np.pi * 50 * t) + + # Filtering + filtered = stx.dsp.filt.bandpass(signal, fs=fs, low=5, high=30) + low_pass = stx.dsp.filt.lowpass(signal, fs=fs, cutoff=20) + high_pass = stx.dsp.filt.highpass(signal, fs=fs, cutoff=5) + + # Power spectral density + freqs, psd = stx.dsp.psd(signal, fs=fs) + + # Band powers + powers = stx.dsp.band_powers(signal, fs=fs, bands={ + "delta": (1, 4), + "theta": (4, 8), + "alpha": (8, 13), + "beta": (13, 30), + }) + + # Hilbert transform (analytic signal) + analytic = stx.dsp.hilbert(signal) + amplitude = np.abs(analytic) + phase = np.angle(analytic) + + # Wavelet decomposition + coeffs = stx.dsp.wavelet(signal, fs=fs, freqs=np.arange(1, 50)) + +Available Functions +------------------- + +**Filtering** (``stx.dsp.filt``) + +- ``bandpass(signal, fs, low, high)`` +- ``lowpass(signal, fs, cutoff)`` +- ``highpass(signal, fs, cutoff)`` +- ``notch(signal, fs, freq)`` + +**Spectral Analysis** + +- ``psd(signal, fs)`` -- Power spectral density +- ``band_powers(signal, fs, bands)`` -- Power in frequency bands +- ``wavelet(signal, fs, freqs)`` -- Continuous wavelet transform + +**Time-Frequency** + +- ``hilbert(signal)`` -- Analytic signal via Hilbert transform +- ``pac(signal, fs)`` -- Phase-amplitude coupling + +**Utilities** + +- ``demo_sig(fs, duration)`` -- Generate demo signals for testing +- ``crop(signal, start, end, fs)`` -- Crop signal to time range +- ``detect_ripples(signal, fs)`` -- Detect sharp-wave ripples + +API Reference +------------- + +See :doc:`/api/scitex.dsp` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/etc.rst.txt b/src/scitex/_sphinx_html/_sources/modules/etc.rst.txt new file mode 100644 index 000000000..485270b22 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/etc.rst.txt @@ -0,0 +1,4 @@ +etc Module (``stx.etc``) +======================== + +See :doc:`/api/scitex.etc` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/events.rst.txt b/src/scitex/_sphinx_html/_sources/modules/events.rst.txt new file mode 100644 index 000000000..098e8048d --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/events.rst.txt @@ -0,0 +1,4 @@ +events Module (``stx.events``) +============================== + +See :doc:`/api/scitex.events` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/gen.rst.txt b/src/scitex/_sphinx_html/_sources/modules/gen.rst.txt new file mode 100644 index 000000000..48539cbe7 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/gen.rst.txt @@ -0,0 +1,36 @@ +Gen Module (``stx.gen``) +======================== + +General utilities for path management, shell commands, and system operations. + +.. note:: + + Session management has moved to ``@stx.session`` (see :doc:`/core_concepts`). + The ``scitex.gen.start()`` / ``scitex.gen.close()`` pattern is deprecated. + +Quick Reference +--------------- + +.. code-block:: python + + import scitex as stx + + # Timestamped output directories + path = stx.gen.mk_spath("./results") + # → ./results/20260213_143022/ + + # Run shell commands + stx.gen.run("ls -la") + + # Clipboard operations + stx.gen.copy("text to clipboard") + text = stx.gen.paste() + + # String to valid path + stx.gen.title2path("My Experiment #1") + # → "my_experiment_1" + +API Reference +------------- + +See :doc:`/api/scitex.gen` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/gists.rst.txt b/src/scitex/_sphinx_html/_sources/modules/gists.rst.txt new file mode 100644 index 000000000..1adc47fff --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/gists.rst.txt @@ -0,0 +1,4 @@ +gists Module (``stx.gists``) +============================ + +See :doc:`/api/scitex.gists` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/git.rst.txt b/src/scitex/_sphinx_html/_sources/modules/git.rst.txt new file mode 100644 index 000000000..a6a0e0d7e --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/git.rst.txt @@ -0,0 +1,4 @@ +git Module (``stx.git``) +======================== + +See :doc:`/api/scitex.git` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/index.rst.txt b/src/scitex/_sphinx_html/_sources/modules/index.rst.txt new file mode 100644 index 000000000..0c60cea18 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/index.rst.txt @@ -0,0 +1,111 @@ +Module Overview +=============== + +SciTeX is organized into focused modules. +All modules are accessible via ``import scitex as stx`` followed by ``stx.``. + +.. toctree:: + :maxdepth: 2 + :caption: Core + + session + io + config + logging + repro + clew + +.. toctree:: + :maxdepth: 2 + :caption: Science & Analysis + + stats + plt + dsp + diagram + canvas + +.. toctree:: + :maxdepth: 2 + :caption: Literature & Writing + + scholar + writer + notebook + +.. toctree:: + :maxdepth: 2 + :caption: Machine Learning + + ai + nn + torch + cv + benchmark + +.. toctree:: + :maxdepth: 2 + :caption: Data & I/O + + pd + db + dataset + schema + +.. toctree:: + :maxdepth: 2 + :caption: Infrastructure + + app + cloud + container + tunnel + cli + browser + capture + audio + notify + social + +.. toctree:: + :maxdepth: 2 + :caption: Utilities + + gen + template + decorators + introspect + str + dict + path + os + sh + git + parallel + linalg + datetime + types + rng + context + resource + utils + etc + web + msword + tex + bridge + compat + module + gists + media + security + ui + dev + audit + +.. toctree:: + :maxdepth: 2 + :caption: Other + + events + project diff --git a/src/scitex/_sphinx_html/_sources/modules/introspect.rst.txt b/src/scitex/_sphinx_html/_sources/modules/introspect.rst.txt new file mode 100644 index 000000000..e7fe4fa1a --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/introspect.rst.txt @@ -0,0 +1,70 @@ +Introspect Module (``stx.introspect``) +======================================= + +IPython-like code inspection for exploring Python packages. + +Quick Reference +--------------- + +.. code-block:: python + + import scitex as stx + + # Function signature (like IPython's func?) + stx.introspect.q("scitex.stats.test_ttest_ind") + # → name, signature, parameters, return type + + # Full source code (like IPython's func??) + stx.introspect.qq("scitex.stats.test_ttest_ind") + # → complete source with line numbers + + # List module members (like enhanced dir()) + stx.introspect.dir("scitex.plt", filter="public", kind="functions") + + # Recursive API tree + df = stx.introspect.list_api("scitex", max_depth=2) + +IPython-Style Shortcuts +----------------------- + +- ``q(dotted_path)`` -- Signature and parameters (like ``func?``) +- ``qq(dotted_path)`` -- Full source code (like ``func??``) +- ``dir(dotted_path, filter, kind)`` -- List members with filtering + +Filters: ``"public"``, ``"private"``, ``"dunder"``, ``"all"`` + +Kinds: ``"functions"``, ``"classes"``, ``"data"``, ``"modules"`` + +Documentation +------------- + +- ``get_docstring(path, format)`` -- Extract docstrings (``"raw"``, ``"parsed"``, ``"summary"``) +- ``get_exports(path)`` -- Get ``__all__`` exports +- ``find_examples(path)`` -- Find usage examples in tests/examples + +Type Analysis +------------- + +- ``get_type_hints_detailed(path)`` -- Full type annotation analysis +- ``get_class_hierarchy(path)`` -- Inheritance tree (MRO + subclasses) +- ``get_class_annotations(path)`` -- Class variable annotations + +Code Analysis +------------- + +- ``get_imports(path, categorize)`` -- All imports (AST-based, grouped by stdlib/third-party/local) +- ``get_dependencies(path, recursive)`` -- Module dependency tree +- ``get_call_graph(path, max_depth)`` -- Function call graph (with timeout protection) + +API Tree +-------- + +.. code-block:: python + + # Generate full module tree as DataFrame + df = stx.introspect.list_api("scitex", max_depth=3, docstring=True) + +API Reference +------------- + +See :doc:`/api/scitex.introspect` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/io.rst.txt b/src/scitex/_sphinx_html/_sources/modules/io.rst.txt new file mode 100644 index 000000000..222150bb8 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/io.rst.txt @@ -0,0 +1,115 @@ +IO Module (``stx.io``) +====================== + +Universal file I/O through ``stx.io.save()`` and ``stx.io.load()``. +Format is determined by file extension. + +Quick Reference +--------------- + +.. code-block:: python + + import scitex as stx + + # Save + stx.io.save(data, "output.csv") # DataFrame → CSV + stx.io.save(arr, "output.npy") # ndarray → NumPy + stx.io.save(fig, "output.png") # Figure → PNG + CSV + YAML recipe + stx.io.save(obj, "output.pkl") # any object → pickle + stx.io.save(d, "output.yaml") # dict → YAML + stx.io.save(d, "output.json") # dict → JSON + + # Load + data = stx.io.load("input.csv") # → DataFrame + arr = stx.io.load("input.npy") # → ndarray + obj = stx.io.load("input.pkl") # → original object + d = stx.io.load("input.yaml") # → dict + +Supported Formats +----------------- + +.. list-table:: + :header-rows: 1 + + * - Extension + - Save Type + - Load Return + * - ``.csv`` + - DataFrame, dict, ndarray + - DataFrame + * - ``.npy`` + - ndarray + - ndarray + * - ``.npz`` + - dict of ndarrays + - NpzFile + * - ``.pkl`` / ``.pickle`` + - any Python object + - original object + * - ``.yaml`` / ``.yml`` + - dict + - dict + * - ``.json`` + - dict, list + - dict or list + * - ``.png`` / ``.jpg`` / ``.svg`` / ``.pdf`` / ``.tiff`` + - matplotlib Figure + - ndarray (image) or Figure + * - ``.hdf5`` / ``.h5`` + - dict, ndarray + - dict or ndarray + * - ``.mat`` + - dict + - dict + * - ``.pth`` / ``.pt`` + - PyTorch state_dict + - dict + * - ``.parquet`` + - DataFrame + - DataFrame + * - ``.txt`` + - str + - str + +Figure Saving +------------- + +When saving a matplotlib Figure, ``stx.io.save`` automatically: + +1. Saves the image file (``.png``, ``.svg``, etc.) +2. Exports a ``.csv`` with the plotted data extracted from axes +3. Exports a ``.yaml`` recipe for reproducing the figure + +.. code-block:: python + + fig, ax = stx.plt.subplots() + ax.plot_line(x, y, label="signal") + stx.io.save(fig, "results/plot.png") + # Creates: + # results/plot.png (the figure) + # results/plot.csv (x, y data) + # results/plot.yaml (recipe for stx.plt.reproduce) + +Provenance Tracking +------------------- + +Inside ``@stx.session``, every ``save`` and ``load`` records file hashes +to a local SQLite database for reproducibility verification. + +.. code-block:: python + + @stx.session + def main(logger=stx.INJECTED, **kw): + data = stx.io.load("input.csv") # hash recorded as input + stx.io.save(result, "output.csv") # hash recorded as output + +Other Functions +--------------- + +- ``stx.io.glob(pattern)`` -- Find files matching a glob pattern +- ``stx.io.load_configs(config_dir)`` -- Load and merge YAML config files + +API Reference +------------- + +See :doc:`/api/scitex.io` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/linalg.rst.txt b/src/scitex/_sphinx_html/_sources/modules/linalg.rst.txt new file mode 100644 index 000000000..fbc5a46ca --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/linalg.rst.txt @@ -0,0 +1,4 @@ +linalg Module (``stx.linalg``) +============================== + +See :doc:`/api/scitex.linalg` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/linter.rst.txt b/src/scitex/_sphinx_html/_sources/modules/linter.rst.txt new file mode 100644 index 000000000..bdcde77b9 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/linter.rst.txt @@ -0,0 +1,131 @@ +Linter Module (``stx.linter``) +=============================== + +AST-based Python linter enforcing SciTeX conventions for reproducible +scientific code. + +.. note:: + + ``stx.linter`` wraps the standalone + `scitex-linter `_ package. + Install with: ``pip install scitex-linter``. + +Quick Reference +--------------- + +.. code-block:: bash + + # Check a file + scitex linter check my_script.py + + # Check with specific severity + scitex linter check my_script.py --severity warning + + # List all rules + scitex linter list-rules + + # Filter by category + scitex linter list-rules --category session + +.. code-block:: python + + from scitex_linter import check_file, list_rules + + # Check a file + results = check_file("my_script.py") + + # List available rules + rules = list_rules() + +Rule Categories +--------------- + +45 rules across 8 categories: + +.. list-table:: + :header-rows: 1 + :widths: 15 25 60 + + * - Prefix + - Category + - Examples + * - ``STX-S`` + - **Session** + - Missing ``@stx.session``, missing ``if __name__`` guard, missing return + * - ``STX-I`` + - **Import** + - Using ``import mngs`` instead of ``import scitex``, bare ``import numpy`` + * - ``STX-IO`` + - **I/O** + - Using ``open()`` instead of ``stx.io``, hardcoded paths + * - ``STX-P`` + - **Plotting** + - Using ``plt.show()`` instead of ``stx.io.save``, missing ``set_xyt`` + * - ``STX-ST`` + - **Statistics** + - Using ``scipy.stats`` directly instead of ``stx.stats`` + * - ``STX-PA`` + - **Path** + - Hardcoded absolute paths, missing ``stx.gen.mk_spath`` + * - ``STX-FM`` + - **Format** + - Non-snake_case names, magic numbers, missing docstrings + +Severity Levels +--------------- + +- **error** -- Must fix (breaks reproducibility or correctness) +- **warning** -- Should fix (best practice violations) +- **info** -- Suggestions for improvement + +Interfaces +---------- + +The linter is available through multiple interfaces: + +**CLI:** + +.. code-block:: bash + + scitex linter check script.py + scitex-linter check script.py # standalone CLI + +**Python API:** + +.. code-block:: python + + from scitex_linter import check_file, check_source + + results = check_file("script.py") + results = check_source("import numpy as np\n...") + +**Flake8 Plugin:** + +.. code-block:: bash + + flake8 --select=STX script.py + +**MCP Tools:** + +.. code-block:: python + + # Available as MCP tools for AI agents + linter_check(path="script.py", severity="warning") + linter_list_rules(category="session") + linter_check_source(source="...", filepath="") + +Configuration +------------- + +Configure via ``pyproject.toml``: + +.. code-block:: toml + + [tool.scitex-linter] + severity = "warning" + ignore = ["STX-S002", "STX-FM001"] + +API Reference +------------- + +See :doc:`/api/scitex.linter` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/logging.rst.txt b/src/scitex/_sphinx_html/_sources/modules/logging.rst.txt new file mode 100644 index 000000000..299227548 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/logging.rst.txt @@ -0,0 +1,107 @@ +Logging Module (``stx.logging``) +================================= + +Unified logging with file/console output, custom warnings, exception +hierarchy, and stream redirection. + +Quick Reference +--------------- + +.. code-block:: python + + import scitex as stx + from scitex import logging + + # Get a logger + logger = logging.getLogger(__name__) + logger.info("Processing data") + logger.success("Analysis complete") # Custom level (31) + logger.fail("Model diverged") # Custom level (35) + + # Configure globally + logging.configure(level="DEBUG", log_file="./run.log") + logging.set_level("WARNING") + + # Temporary file logging + with logging.log_to_file("analysis.log"): + logger.info("This goes to both console and file") + + # Warnings + logging.warn("Large dataset", category=logging.PerformanceWarning) + logging.warn_deprecated("old_func", "new_func", version="3.0") + logging.filterwarnings("ignore", category=logging.UnitWarning) + +Log Levels +---------- + +.. list-table:: + :header-rows: 1 + :widths: 20 15 65 + + * - Level + - Value + - Description + * - ``DEBUG`` + - 10 + - Detailed diagnostic information + * - ``INFO`` + - 20 + - General operational messages + * - ``WARNING`` + - 30 + - Something unexpected but not fatal + * - ``SUCCESS`` + - 31 + - Custom: operation completed successfully + * - ``FAIL`` + - 35 + - Custom: operation failed (non-fatal) + * - ``ERROR`` + - 40 + - Serious problem + * - ``CRITICAL`` + - 50 + - Program may not continue + +Warning Categories +------------------ + +- ``SciTeXWarning`` -- Base warning class +- ``UnitWarning`` -- SI unit convention issues +- ``StyleWarning`` -- Formatting issues +- ``SciTeXDeprecationWarning`` -- Deprecated features +- ``PerformanceWarning`` -- Performance issues +- ``DataLossWarning`` -- Potential data loss + +Exception Hierarchy +------------------- + +All inherit from ``SciTeXError``: + +- **I/O**: ``IOError``, ``FileFormatError``, ``SaveError``, ``LoadError`` +- **Config**: ``ConfigurationError``, ``ConfigFileNotFoundError``, ``ConfigKeyError`` +- **Path**: ``PathError``, ``InvalidPathError``, ``PathNotFoundError`` +- **Data**: ``DataError``, ``ShapeError``, ``DTypeError`` +- **Plotting**: ``PlottingError``, ``FigureNotFoundError``, ``AxisError`` +- **Stats**: ``StatsError``, ``TestError`` +- **Scholar**: ``ScholarError``, ``SearchError``, ``PDFDownloadError``, ``DOIResolutionError`` +- **NN**: ``NNError``, ``ModelError`` +- **Template**: ``TemplateError``, ``TemplateViolationError`` + +Stream Redirection +------------------ + +.. code-block:: python + + from scitex.logging import tee + import sys + + # Redirect stdout/stderr to log files + sys.stdout, sys.stderr = tee(sys, sdir="./output") + +The ``@stx.session`` decorator handles this automatically. + +API Reference +------------- + +See :doc:`/api/scitex.logging` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/media.rst.txt b/src/scitex/_sphinx_html/_sources/modules/media.rst.txt new file mode 100644 index 000000000..22313640a --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/media.rst.txt @@ -0,0 +1,4 @@ +media Module (``stx.media``) +============================ + +See :doc:`/api/scitex.media` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/module.rst.txt b/src/scitex/_sphinx_html/_sources/modules/module.rst.txt new file mode 100644 index 000000000..1c9d3aa8b --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/module.rst.txt @@ -0,0 +1,4 @@ +module Module (``stx.module``) +============================== + +See :doc:`/api/scitex.module` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/msword.rst.txt b/src/scitex/_sphinx_html/_sources/modules/msword.rst.txt new file mode 100644 index 000000000..9254b4da2 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/msword.rst.txt @@ -0,0 +1,4 @@ +msword Module (``stx.msword``) +============================== + +See :doc:`/api/scitex.msword` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/nn.rst.txt b/src/scitex/_sphinx_html/_sources/modules/nn.rst.txt new file mode 100644 index 000000000..0239f5b86 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/nn.rst.txt @@ -0,0 +1,79 @@ +NN Module (``stx.nn``) +====================== + +PyTorch neural network layers for signal processing and neuroscience +applications. + +Quick Reference +--------------- + +.. code-block:: python + + import scitex as stx + import torch + + # Bandpass filtering as a differentiable layer + bpf = stx.nn.BandPassFilter( + bands=[[4, 8], [8, 13], [13, 30]], # theta, alpha, beta + fs=256, seq_len=1024 + ) + filtered = bpf(signal) # (batch, channels, 3, 1024) + + # Power spectral density + psd = stx.nn.PSD(sample_rate=256) + power, freqs = psd(signal) + + # Phase-amplitude coupling + pac = stx.nn.PAC(seq_len=1024, fs=256) + coupling = pac(signal) + +Signal Processing Layers +------------------------- + +**Filtering** (all differentiable): + +- ``BandPassFilter(bands, fs, seq_len)`` -- Multi-band frequency filtering +- ``BandStopFilter(bands, fs, seq_len)`` -- Reject frequency bands +- ``LowPassFilter(cutoffs_hz, fs, seq_len)`` -- Anti-aliasing / smoothing +- ``HighPassFilter(cutoffs_hz, fs, seq_len)`` -- High-frequency emphasis +- ``GaussianFilter(sigma)`` -- Gaussian kernel smoothing +- ``DifferentiableBandPassFilter(...)`` -- Learnable bandpass parameters + +**Spectral Analysis**: + +- ``Spectrogram(sampling_rate, n_fft)`` -- STFT-based magnitude spectrogram +- ``PSD(sample_rate, prob)`` -- FFT-based power spectral density +- ``Wavelet(samp_rate, freq_scale)`` -- Continuous wavelet transform + +**Phase & Coupling**: + +- ``Hilbert(seq_len)`` -- Analytic signal (phase + amplitude) +- ``ModulationIndex(n_bins)`` -- Phase-amplitude coupling metric +- ``PAC(seq_len, fs, ...)`` -- Complete PAC analysis pipeline + +Channel Manipulation +-------------------- + +- ``SwapChannels()`` -- Random channel permutation (training augmentation) +- ``DropoutChannels(dropout)`` -- Drop entire channels +- ``ChannelGainChanger(n_chs)`` -- Learnable per-channel scaling +- ``FreqGainChanger(n_bands, fs)`` -- Learnable per-band scaling + +Attention & Shape +----------------- + +- ``SpatialAttention(n_chs_in)`` -- Adaptive channel weighting +- ``TransposeLayer(axis1, axis2)`` -- Dimension permutation +- ``AxiswiseDropout(dropout_prob, dim)`` -- Drop entire axis + +Architectures +------------- + +- ``ResNet1D(n_chs, n_out, n_blks)`` -- 1D residual network +- ``BNet`` / ``BNet_Res`` -- Multi-head EEG classifier +- ``MNet1000`` -- 2D CNN feature extractor + +API Reference +------------- + +See :doc:`/api/scitex.nn` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/notebook.rst.txt b/src/scitex/_sphinx_html/_sources/modules/notebook.rst.txt new file mode 100644 index 000000000..00b17aa97 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/notebook.rst.txt @@ -0,0 +1,4 @@ +notebook Module (``stx.notebook``) +================================== + +See :doc:`/api/scitex.notebook` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/notify.rst.txt b/src/scitex/_sphinx_html/_sources/modules/notify.rst.txt new file mode 100644 index 000000000..32373f3c8 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/notify.rst.txt @@ -0,0 +1,4 @@ +notify Module (``stx.notify``) +============================== + +See :doc:`/api/scitex.notify` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/os.rst.txt b/src/scitex/_sphinx_html/_sources/modules/os.rst.txt new file mode 100644 index 000000000..b6405d019 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/os.rst.txt @@ -0,0 +1,4 @@ +os Module (``stx.os``) +====================== + +See :doc:`/api/scitex.os` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/parallel.rst.txt b/src/scitex/_sphinx_html/_sources/modules/parallel.rst.txt new file mode 100644 index 000000000..24942b28c --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/parallel.rst.txt @@ -0,0 +1,4 @@ +parallel Module (``stx.parallel``) +================================== + +See :doc:`/api/scitex.parallel` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/path.rst.txt b/src/scitex/_sphinx_html/_sources/modules/path.rst.txt new file mode 100644 index 000000000..ca9d7d7cd --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/path.rst.txt @@ -0,0 +1,4 @@ +path Module (``stx.path``) +========================== + +See :doc:`/api/scitex.path` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/pd.rst.txt b/src/scitex/_sphinx_html/_sources/modules/pd.rst.txt new file mode 100644 index 000000000..540d969bd --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/pd.rst.txt @@ -0,0 +1,45 @@ +PD Module (``stx.pd``) +====================== + +Pandas DataFrame helper functions for common transformations. + +Quick Reference +--------------- + +.. code-block:: python + + import scitex as stx + import pandas as pd + + df = pd.DataFrame({ + "subject": ["A", "A", "B", "B"], + "condition": ["ctrl", "exp", "ctrl", "exp"], + "score": [10, 15, 12, 18], + }) + + # Find individual (unique) values + subjects = stx.pd.find_indi(df, "subject") + + # Convert to long format + melted = stx.pd.melt_cols(df, id_vars=["subject"]) + + # Merge columns + merged = stx.pd.merge_cols(df, cols=["subject", "condition"], sep="_") + + # Force to DataFrame + stx.pd.force_df({"a": [1, 2], "b": [3, 4]}) + +Available Functions +------------------- + +- ``find_indi(df, col)`` -- Find unique individual identifiers +- ``find_pval(df)`` -- Find p-value columns in a DataFrame +- ``force_df(data)`` -- Convert any data to a DataFrame +- ``melt_cols(df, id_vars)`` -- Melt columns to long format +- ``merge_cols(df, cols, sep)`` -- Merge multiple columns into one +- ``to_xyz(df, x, y, z)`` -- Reshape to x, y, z pivot format + +API Reference +------------- + +See :doc:`/api/scitex.pd` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/plt.rst.txt b/src/scitex/_sphinx_html/_sources/modules/plt.rst.txt new file mode 100644 index 000000000..41a0c4b1f --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/plt.rst.txt @@ -0,0 +1,161 @@ +PLT Module (``stx.plt``) +======================== + +Publication-ready figures powered by `figrecipe `_. + +.. note:: + + ``stx.plt`` wraps matplotlib via the **figrecipe** package, adding + mm-based layout, data-tracking axes, auto CSV export, and reproducible + YAML recipes. The standalone package can also be used directly: + ``pip install figrecipe``. + +Quick Reference +--------------- + +.. code-block:: python + + import scitex as stx + + # Create figure (returns FigWrapper, AxisWrapper) + fig, ax = stx.plt.subplots() + fig, axes = stx.plt.subplots(2, 2) + + # Data-tracking plot methods (all record data for CSV export) + ax.plot_line(x, y, label="signal") + ax.plot_scatter(x, y, alpha=0.5) + ax.plot_bar(labels, values) + ax.plot_heatmap(matrix) + ax.plot_violin(groups) + ax.plot_box(groups) + ax.plot_kde(data) + + # Axis helper (xlabel, ylabel, title in one call) + ax.set_xyt("Time (s)", "Amplitude (mV)", "EEG Signal") + + # Save (auto-exports CSV + YAML recipe) + stx.io.save(fig, "figure.png") + +Plot Types (47) +--------------- + +All ``ax.plot_*`` methods track their input data so it can be exported as CSV. + +.. list-table:: + :header-rows: 1 + :widths: 25 75 + + * - Category + - Types + * - **Line & Curve** + - ``plot``, ``step``, ``fill``, ``fill_between``, ``fill_betweenx``, ``errorbar``, ``stackplot``, ``stairs`` + * - **Scatter & Points** + - ``scatter`` + * - **Bar & Categorical** + - ``bar``, ``barh`` + * - **Distribution** + - ``hist``, ``hist2d``, ``boxplot``, ``violinplot``, ``ecdf`` + * - **2D Image & Matrix** + - ``imshow``, ``matshow``, ``pcolor``, ``pcolormesh``, ``hexbin``, ``spy`` + * - **Contour & Surface** + - ``contour``, ``contourf``, ``tricontour``, ``tricontourf``, ``tripcolor``, ``triplot`` + * - **Spectral & Signal** + - ``specgram``, ``psd``, ``csd``, ``cohere``, ``angle_spectrum``, ``magnitude_spectrum``, ``phase_spectrum``, ``acorr``, ``xcorr`` + * - **Vector & Flow** + - ``quiver``, ``barbs``, ``streamplot`` + * - **Special** + - ``pie``, ``stem``, ``eventplot``, ``loglog``, ``semilogx``, ``semilogy``, ``graph`` + +Standard matplotlib methods (``ax.plot``, ``ax.scatter``, etc.) also work +and are tracked. + +MM-Based Layout +--------------- + +Figures use millimeter dimensions for precise control: + +.. code-block:: python + + fig, ax = stx.plt.subplots( + axes_width_mm=80, + axes_height_mm=60, + ) + + # Multi-panel with exact spacing + fig, axes = stx.plt.subplots( + nrows=2, ncols=3, + axes_width_mm=50, + axes_height_mm=40, + ) + +Axis Helpers +------------ + +.. code-block:: python + + # Set xlabel, ylabel, title in one call + ax.set_xyt("X Label", "Y Label", "Title") + + # Automatic axis label formatting with units + ax.set_xyt("Frequency (Hz)", "Power (dB)", "Power Spectrum") + +Figure Composition +------------------ + +Combine multiple figures into panels: + +.. code-block:: python + + # Compose panels A, B, C + stx.plt.compose( + sources=["fig_a.png", "fig_b.png", "fig_c.png"], + output_path="combined.png", + layout="horizontal", + panel_labels=True, + ) + +Statistical Annotations +----------------------- + +Add significance brackets to figures: + +.. code-block:: python + + # In the declarative spec + stat_annotations: + - x1: 0 + x2: 1 + y: 5.5 + text: "***" + - x1: 0 + x2: 2 + y: 6.5 + p_value: 0.02 + style: stars + +Reproducible Figures +-------------------- + +When saved via ``stx.io.save``, figures get a ``.yaml`` recipe. +Reproduce any figure from its recipe: + +.. code-block:: python + + stx.plt.reproduce("figure.yaml", output_path="reproduced.png") + +Style Presets +------------- + +.. code-block:: python + + import figrecipe as fr + + fr.load_style("SCITEX") # Publication defaults + fr.load_style("MATPLOTLIB") # Standard matplotlib + +See the :doc:`/gallery` for visual examples. + +API Reference +------------- + +See :doc:`/api/scitex.plt` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/project.rst.txt b/src/scitex/_sphinx_html/_sources/modules/project.rst.txt new file mode 100644 index 000000000..10402cc17 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/project.rst.txt @@ -0,0 +1,4 @@ +project Module (``stx.project``) +================================ + +See :doc:`/api/scitex.project` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/repro.rst.txt b/src/scitex/_sphinx_html/_sources/modules/repro.rst.txt new file mode 100644 index 000000000..2c7789763 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/repro.rst.txt @@ -0,0 +1,70 @@ +Repro Module (``stx.repro``) +============================ + +Reproducibility utilities: random state management, ID generation, +timestamps, and array hashing. + +Quick Reference +--------------- + +.. code-block:: python + + import scitex as stx + + # Fix all random seeds (numpy, torch, random, ...) + rng = stx.repro.get() # Global manager (seed=42) + rng = stx.repro.reset(seed=123) # Reset with new seed + + # Named generators for deterministic results + data_gen = rng.get_np_generator("data") + data = data_gen.random(100) # Same seed+name = same result + + # Unique identifiers + stx.repro.gen_id() + # → "2026Y-02M-13D-14h30m15s_a3Bc9xY2" + + stx.repro.gen_timestamp() + # → "2026-0213-1430" + + # Verify reproducibility + rng.verify(data, "train_data") # First: caches hash + rng.verify(data, "train_data") # Later: verifies match + +RandomStateManager +------------------ + +Central class for managing random states across libraries. + +.. code-block:: python + + rng = stx.repro.RandomStateManager(seed=42) + + # Named generators (same name + seed = deterministic) + np_gen = rng.get_np_generator("experiment") + torch_gen = rng.get_torch_generator("model") + + # Checkpoint and restore + rng.checkpoint("before_training") + rng.restore("before_training.pkl") + + # Temporary seed change + with rng.temporary_seed(999): + noise = rng.get_np_generator("noise").random(10) + +Automatically fixes seeds for: ``random``, ``numpy``, ``torch`` (+ CUDA), +``tensorflow``, ``jax``. + +Available Functions +------------------- + +- ``get(verbose)`` -- Get or create global RandomStateManager singleton +- ``reset(seed, verbose)`` -- Reset global instance with new seed +- ``fix_seeds(seed, ...)`` -- Legacy function (use RandomStateManager instead) +- ``gen_id(time_format, N)`` -- Generate unique timestamp + random ID +- ``gen_timestamp()`` -- Generate timestamp string for file naming +- ``hash_array(array_data)`` -- SHA256 hash of numpy array (16 chars) + +API Reference +------------- + +See :doc:`/api/scitex.repro` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/resource.rst.txt b/src/scitex/_sphinx_html/_sources/modules/resource.rst.txt new file mode 100644 index 000000000..d5b88ca76 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/resource.rst.txt @@ -0,0 +1,4 @@ +resource Module (``stx.resource``) +================================== + +See :doc:`/api/scitex.resource` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/rng.rst.txt b/src/scitex/_sphinx_html/_sources/modules/rng.rst.txt new file mode 100644 index 000000000..31ed089f2 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/rng.rst.txt @@ -0,0 +1,4 @@ +rng Module (``stx.rng``) +======================== + +See :doc:`/api/scitex.rng` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/schema.rst.txt b/src/scitex/_sphinx_html/_sources/modules/schema.rst.txt new file mode 100644 index 000000000..12c215622 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/schema.rst.txt @@ -0,0 +1,4 @@ +schema Module (``stx.schema``) +============================== + +See :doc:`/api/scitex.schema` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/scholar.rst.txt b/src/scitex/_sphinx_html/_sources/modules/scholar.rst.txt new file mode 100644 index 000000000..42ec9fc23 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/scholar.rst.txt @@ -0,0 +1,131 @@ +Scholar Module (``stx.scholar``) +================================= + +Literature management: search papers, download PDFs, enrich BibTeX, +and organize a local library across multiple projects. + +Quick Reference +--------------- + +.. code-block:: python + + from scitex.scholar import Scholar + + scholar = Scholar(project="my_research") + + # Load and enrich BibTeX + papers = scholar.load_bibtex("references.bib") + enriched = scholar.enrich_papers(papers) + # Adds: DOIs, abstracts, citation counts, impact factors + + # Save to library and export + scholar.save_papers_to_library(enriched) + scholar.save_papers_as_bibtex(enriched, "enriched.bib") + + # Search your library + results = scholar.search_library("neural oscillations") + + # Download PDFs + scholar.download_pdfs(dois, output_dir) + +CLI Usage +--------- + +.. code-block:: bash + + # Full pipeline from BibTeX + scitex scholar bibtex refs.bib --project myresearch --num-workers 8 + + # Search papers + scitex scholar search "deep learning EEG" + + # Download PDFs + scitex scholar download --doi 10.1038/nature12373 + + # Institutional authentication + scitex scholar auth --method openathens + scitex scholar auth --method shibboleth --institution "MIT" + +Data Sources +------------ + +Searches and enriches from: + +- **CrossRef** (167M+ papers) -- DOI resolution, citation counts +- **Semantic Scholar** -- Abstracts, references, influence scores +- **PubMed** -- Biomedical literature +- **arXiv** -- Preprints +- **OpenAlex** (284M+ works) -- Open metadata + +Key Classes +----------- + +- ``Scholar`` -- Main entry point (search, enrich, download, organize) +- ``Paper`` -- Type-safe metadata container (Pydantic model) +- ``Papers`` -- Collection with filtering, sorting, and export +- ``ScholarConfig`` -- YAML-based configuration +- ``ScholarLibrary`` -- Local library storage and caching + +Paper Metadata +-------------- + +Each ``Paper`` contains structured metadata sections: + +.. code-block:: python + + paper.metadata.basic # title, authors, year, abstract, keywords + paper.metadata.id # DOI, arXiv, PMID, Semantic Scholar ID + paper.metadata.publication # journal, impact factor, volume, issue + paper.metadata.citation_count # total + yearly breakdown (2015--2024) + paper.metadata.url # DOI URL, publisher, arXiv, PDFs + paper.metadata.access # open access status, license + +Filtering and Sorting +--------------------- + +.. code-block:: python + + # Criteria-based filtering + recent = papers.filter(year_min=2020, has_doi=True) + elite = papers.filter(min_impact_factor=10, min_citations=500) + + # Lambda filtering + custom = papers.filter(lambda p: "EEG" in (p.metadata.basic.title or "")) + + # Sorting + papers.sort_by("year", reverse=True) + papers.sort_by("citation_count", reverse=True) + + # Chaining + top_recent = papers.filter(year_min=2020).sort_by("citation_count", reverse=True) + +Project Organization +-------------------- + +.. code-block:: python + + scholar = Scholar(project="review_paper") + scholar.list_projects() + papers = scholar.load_project() + + # Export to multiple formats + scholar.save_papers_as_bibtex(papers, "output.bib") + papers.to_dataframe() # pandas DataFrame + +Storage Architecture +-------------------- + +.. code-block:: text + + ~/.scitex/scholar/library/ + +-- MASTER/ # Centralized master storage + | +-- 8DIGIT01/ # Hash-based unique ID from DOI + | | +-- metadata.json + | | +-- paper.pdf + +-- project_name/ # Project-specific symlinks + +-- Author-Year-Journal -> ../MASTER/8DIGIT01 + +API Reference +------------- + +See :doc:`/api/scitex.scholar` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/security.rst.txt b/src/scitex/_sphinx_html/_sources/modules/security.rst.txt new file mode 100644 index 000000000..36f9a0b7a --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/security.rst.txt @@ -0,0 +1,4 @@ +security Module (``stx.security``) +================================== + +See :doc:`/api/scitex.security` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/session.rst.txt b/src/scitex/_sphinx_html/_sources/modules/session.rst.txt new file mode 100644 index 000000000..6b2f7a491 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/session.rst.txt @@ -0,0 +1,210 @@ +Session Decorator (``@stx.session``) +===================================== + +The ``@stx.session`` decorator is the primary entry point for SciTeX +scripts. It wraps a function to provide automatic CLI generation, +output directory management, configuration injection, and provenance tracking. + +Basic Usage +----------- + +.. code-block:: python + + import scitex as stx + + @stx.session + def main( + data_path="data.csv", # CLI: --data-path data.csv + threshold=0.5, # CLI: --threshold 0.7 + CONFIG=stx.INJECTED, # Auto-injected session config + plt=stx.INJECTED, # Pre-configured matplotlib + logger=stx.INJECTED, # Session logger + ): + """Analyze experimental data.""" + data = stx.io.load(data_path) + result = process(data, threshold) + stx.io.save(result, "output.csv") + return 0 # exit code (0 = success) + + if __name__ == "__main__": + main() # No arguments triggers CLI mode + +Run from the command line: + +.. code-block:: bash + + python my_script.py --data-path experiment.csv --threshold 0.3 + python my_script.py --help # Shows all parameters with defaults + +How It Works +------------ + +.. image:: ../_static/session_lifecycle.png + :width: 60% + :align: center + :alt: Session lifecycle: main() → Parse CLI → Create dir → Load config → Execute → SUCCESS or ERROR + +When ``main()`` is called **without arguments**, the decorator: + +1. **Parses CLI arguments** from the function signature (type hints and defaults become ``argparse`` options) +2. **Creates a session directory** under ``script_out/RUNNING/{session_id}/`` +3. **Loads configuration** from ``./config/*.yaml`` files into ``CONFIG`` +4. **Injects globals** (``CONFIG``, ``plt``, ``COLORS``, ``logger``, ``rngg``) into the function +5. **Redirects stdout/stderr** to log files +6. **Executes the function** with parsed arguments +7. **Moves output** from ``RUNNING/`` to ``FINISHED_SUCCESS/`` (or ``FINISHED_ERROR/``) + +When called **with arguments** (e.g., ``main(data_path="x.csv")``), the decorator +is bypassed and the function runs directly -- useful for testing and notebooks. + +Output Directory Structure +-------------------------- + +Each run produces a self-contained output directory: + +.. code-block:: text + + my_script_out/ + +-- FINISHED_SUCCESS/ + | +-- 2026Y-02M-13D-14h30m15s_Z5MR/ + | +-- CONFIGS/ + | | +-- CONFIG.yaml # Frozen configuration + | | +-- CONFIG.pkl # Pickled config + | +-- logs/ + | | +-- stdout.log # Captured stdout + | | +-- stderr.log # Captured stderr + | +-- output.csv # Your saved files + | +-- sine.png # Your saved figures + | +-- sine.csv # Auto-exported figure data + +-- FINISHED_ERROR/ + | +-- ... # Runs that returned non-zero + +-- RUNNING/ + +-- ... # Currently active sessions + +The session ID format is ``YYYY-MM-DD-HH:MM:SS_XXXX`` where ``XXXX`` is +a random 4-character suffix for uniqueness. + +Injected Parameters +------------------- + +Use ``stx.INJECTED`` as the default value to receive auto-injected objects: + +.. list-table:: + :header-rows: 1 + :widths: 20 20 60 + + * - Parameter Name + - Type + - Description + * - ``CONFIG`` + - ``DotDict`` + - Session config with ``ID``, ``SDIR_RUN``, ``FILE``, ``ARGS``, plus all ``./config/*.yaml`` values + * - ``plt`` + - module + - ``matplotlib.pyplot`` configured for the session (Agg backend, style settings) + * - ``COLORS`` + - ``DotDict`` + - Color palette for consistent plotting + * - ``logger`` + - Logger + - SciTeX logger writing to session log files + * - ``rngg`` + - ``RandomStateManager`` + - Reproducibility manager (seeds fixed by default) + +Only request the parameters you need: + +.. code-block:: python + + @stx.session + def main(n=100, CONFIG=stx.INJECTED): + """Minimal example -- only CONFIG injected.""" + print(f"Session ID: {CONFIG.ID}") + print(f"Output dir: {CONFIG.SDIR_RUN}") + return 0 + +CONFIG Object +------------- + +The injected ``CONFIG`` is a ``DotDict`` supporting both dictionary and +dot-notation access: + +.. code-block:: python + + CONFIG["MODEL"]["hidden_size"] # dict-style + CONFIG.MODEL.hidden_size # dot-style (equivalent) + +It contains: + +.. code-block:: python + + CONFIG.ID # "2026Y-02M-13D-14h30m15s_Z5MR" + CONFIG.PID # Process ID + CONFIG.FILE # Path to the script + CONFIG.SDIR_OUT # Base output directory + CONFIG.SDIR_RUN # Current session's output directory + CONFIG.START_DATETIME # When the session started + CONFIG.ARGS # Parsed CLI arguments as dict + CONFIG.EXIT_STATUS # 0 (success), 1 (error), or None + +Any YAML files in ``./config/`` are merged into CONFIG: + +.. code-block:: yaml + + # ./config/EXPERIMENT.yaml + learning_rate: 0.001 + batch_size: 32 + +.. code-block:: python + + # Accessible as: + CONFIG.EXPERIMENT.learning_rate # 0.001 + CONFIG.EXPERIMENT.batch_size # 32 + +Decorator Options +----------------- + +.. code-block:: python + + @stx.session(verbose=True, agg=False, notify=True, sdir_suffix="v2") + def main(...): + ... + +.. list-table:: + :header-rows: 1 + :widths: 20 15 15 50 + + * - Option + - Type + - Default + - Description + * - ``verbose`` + - bool + - ``False`` + - Enable verbose logging + * - ``agg`` + - bool + - ``True`` + - Use matplotlib Agg backend (set ``False`` for interactive plots) + * - ``notify`` + - bool + - ``False`` + - Send notification when session completes + * - ``sdir_suffix`` + - str + - ``None`` + - Append suffix to output directory name + +Best Practices +-------------- + +1. **One session per script** -- each ``.py`` file should have one ``@stx.session`` function +2. **Return 0 for success** -- the return value becomes the exit status +3. **Save outputs with** ``stx.io.save`` -- files go into the session directory and are provenance-tracked +4. **Put config in YAML** -- use ``./config/*.yaml`` instead of hardcoding parameters +5. **Use** ``stx.repro.fix_seeds()`` -- already called by default via ``rngg`` + +API Reference +------------- + +See :doc:`/api/scitex.session` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/sh.rst.txt b/src/scitex/_sphinx_html/_sources/modules/sh.rst.txt new file mode 100644 index 000000000..4cc94f3ae --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/sh.rst.txt @@ -0,0 +1,4 @@ +sh Module (``stx.sh``) +====================== + +See :doc:`/api/scitex.sh` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/social.rst.txt b/src/scitex/_sphinx_html/_sources/modules/social.rst.txt new file mode 100644 index 000000000..28daf833d --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/social.rst.txt @@ -0,0 +1,4 @@ +social Module (``stx.social``) +============================== + +See :doc:`/api/scitex.social` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/stats.rst.txt b/src/scitex/_sphinx_html/_sources/modules/stats.rst.txt new file mode 100644 index 000000000..5cf6bbf74 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/stats.rst.txt @@ -0,0 +1,152 @@ +Stats Module (``stx.stats``) +============================ + +23 statistical tests with effect sizes, confidence intervals, and +publication-ready formatting. + +Quick Reference +--------------- + +.. code-block:: python + + import scitex as stx + import numpy as np + + g1 = np.random.normal(0, 1, 50) + g2 = np.random.normal(0.5, 1, 50) + + # Run a test + result = stx.stats.test_ttest_ind(g1, g2) + + # Result is a flat dict with all details + print(result["statistic"]) # t-statistic + print(result["pvalue"]) # p-value + print(result["effect_size"]) # Cohen's d + print(result["stars"]) # Significance stars + + # Get as DataFrame or LaTeX + df = stx.stats.test_ttest_ind(g1, g2, return_as="dataframe") + tex = stx.stats.test_ttest_ind(g1, g2, return_as="latex") + +Available Tests +--------------- + +**Parametric** + +- ``test_ttest_1samp(data, popmean)`` -- One-sample t-test +- ``test_ttest_ind(g1, g2)`` -- Independent two-sample t-test +- ``test_ttest_rel(g1, g2)`` -- Paired t-test +- ``test_anova(*groups)`` -- One-way ANOVA +- ``test_anova_2way(data, factor1, factor2)`` -- Two-way ANOVA +- ``test_anova_rm(data, groups)`` -- Repeated measures ANOVA + +**Non-parametric** + +- ``test_mannwhitneyu(g1, g2)`` -- Mann-Whitney U test +- ``test_wilcoxon(g1, g2)`` -- Wilcoxon signed-rank test +- ``test_kruskal(*groups)`` -- Kruskal-Wallis H test +- ``test_friedman(*groups)`` -- Friedman test +- ``test_brunner_munzel(g1, g2)`` -- Brunner-Munzel test + +**Correlation** + +- ``test_pearson(x, y)`` -- Pearson correlation +- ``test_spearman(x, y)`` -- Spearman rank correlation +- ``test_kendall(x, y)`` -- Kendall tau correlation +- ``test_theilsen(x, y)`` -- Theil-Sen robust regression + +**Categorical** + +- ``test_chi2(observed)`` -- Chi-squared test +- ``test_fisher(table)`` -- Fisher's exact test +- ``test_mcnemar(table)`` -- McNemar test +- ``test_cochran_q(*groups)`` -- Cochran's Q test + +**Normality** + +- ``test_shapiro(data)`` -- Shapiro-Wilk test +- ``test_ks_1samp(data)`` -- One-sample Kolmogorov-Smirnov test +- ``test_ks_2samp(x, y)`` -- Two-sample Kolmogorov-Smirnov test +- ``test_normality(*samples)`` -- Multi-sample normality check + +Seaborn-Style Data Parameter +---------------------------- + +All two-sample and one-sample tests accept an optional ``data`` parameter +for DataFrame/CSV column resolution (like seaborn): + +.. code-block:: python + + import pandas as pd + + df = pd.read_csv("experiment.csv") + + # Two-sample: column names as x/y + result = stx.stats.test_ttest_ind(x="before", y="after", data=df) + + # One-sample: column name as x + result = stx.stats.test_shapiro(x="scores", data=df) + + # Multi-group: value + group columns + result = stx.stats.test_anova(data=df, value_col="score", group_col="treatment") + + # Also works with CSV path + result = stx.stats.test_ttest_ind(x="col1", y="col2", data="data.csv") + +Test Recommendation +------------------- + +Not sure which test to use? Let SciTeX recommend: + +.. code-block:: python + + recommendations = stx.stats.recommend_tests( + n_groups=2, + sample_sizes=[30, 35], + outcome_type="continuous", + paired=False, + ) + +Output Formats +-------------- + +Every test supports ``return_as`` parameter: + +- ``"dict"`` (default) -- Returns plain dict with all results +- ``"dataframe"`` -- Returns pandas DataFrame + +Descriptive Statistics +---------------------- + +.. code-block:: python + + stx.stats.describe(data) # mean, std, median, IQR, etc. + stx.stats.effect_sizes.cohens_d(g1, g2) # Cohen's d + stx.stats.test_normality(g1, g2) # Multi-sample normality + stx.stats.p_to_stars(0.003) # "**" + +Multiple Comparison Correction +------------------------------ + +.. code-block:: python + + corrected = stx.stats.correct_pvalues( + [0.01, 0.03, 0.05, 0.001], + method="fdr_bh", + ) + +Post-hoc Tests +-------------- + +.. code-block:: python + + stx.stats.posthoc_test( + [g1, g2, g3], + group_names=["Control", "Treatment A", "Treatment B"], + method="tukey", + ) + +API Reference +------------- + +See :doc:`/api/scitex.stats` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/str.rst.txt b/src/scitex/_sphinx_html/_sources/modules/str.rst.txt new file mode 100644 index 000000000..1702d22b5 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/str.rst.txt @@ -0,0 +1,4 @@ +str Module (``stx.str``) +======================== + +See :doc:`/api/scitex.str` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/template.rst.txt b/src/scitex/_sphinx_html/_sources/modules/template.rst.txt new file mode 100644 index 000000000..f69f76a14 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/template.rst.txt @@ -0,0 +1,85 @@ +Template Module (``stx.template``) +==================================== + +Project scaffolding and code snippet templates for quick starts. + +Project Templates +----------------- + +.. code-block:: bash + + # Clone a project template + scitex template clone research my_project + scitex template clone pip_project my_package + scitex template clone paper my_paper + +.. list-table:: + :header-rows: 1 + :widths: 20 80 + + * - Template + - Description + * - ``research`` + - Full scientific workflow (scripts, data, docs, config) + * - ``research_minimal`` + - Essential modules only + * - ``pip_project`` + - Distributable Python package for PyPI + * - ``singularity`` + - Reproducible containerized environment + * - ``paper_directory`` + - Academic paper with LaTeX/BibTeX structure + +Git Strategies +-------------- + +.. list-table:: + :header-rows: 1 + :widths: 20 80 + + * - Strategy + - Behavior + * - ``child`` (default) + - Fresh git repo, isolated from template + * - ``parent`` + - Use parent repo if available + * - ``origin`` + - Preserve template's git history + * - ``None`` + - No git initialization + +Code Templates +-------------- + +.. code-block:: bash + + # List available code templates + scitex template code list + + # Get a template + scitex template code session # Full @stx.session script + scitex template code session-minimal # Lightweight session + scitex template code session-plot # Figure-focused session + scitex template code session-stats # Statistics-focused session + scitex template code io # I/O operations + scitex template code plt # Plotting patterns + scitex template code stats # Statistical analysis + scitex template code scholar # Literature management + +Python API +---------- + +.. code-block:: python + + from scitex.template import clone_template, get_code_template + + # Clone project + clone_template("research", "my_experiment", git_strategy="child") + + # Get code snippet + code = get_code_template("session", filepath="analyze.py") + +API Reference +------------- + +See :doc:`/api/scitex.template` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/tex.rst.txt b/src/scitex/_sphinx_html/_sources/modules/tex.rst.txt new file mode 100644 index 000000000..47e28c849 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/tex.rst.txt @@ -0,0 +1,4 @@ +tex Module (``stx.tex``) +======================== + +See :doc:`/api/scitex.tex` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/torch.rst.txt b/src/scitex/_sphinx_html/_sources/modules/torch.rst.txt new file mode 100644 index 000000000..fbcb78586 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/torch.rst.txt @@ -0,0 +1,4 @@ +torch Module (``stx.torch``) +============================ + +See :doc:`/api/scitex.torch` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/tunnel.rst.txt b/src/scitex/_sphinx_html/_sources/modules/tunnel.rst.txt new file mode 100644 index 000000000..7cf5a4dc6 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/tunnel.rst.txt @@ -0,0 +1,4 @@ +tunnel Module (``stx.tunnel``) +============================== + +See :doc:`/api/scitex.tunnel` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/types.rst.txt b/src/scitex/_sphinx_html/_sources/modules/types.rst.txt new file mode 100644 index 000000000..79a96c66c --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/types.rst.txt @@ -0,0 +1,4 @@ +types Module (``stx.types``) +============================ + +See :doc:`/api/scitex.types` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/ui.rst.txt b/src/scitex/_sphinx_html/_sources/modules/ui.rst.txt new file mode 100644 index 000000000..127ad15f8 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/ui.rst.txt @@ -0,0 +1,4 @@ +ui Module (``stx.ui``) +====================== + +See :doc:`/api/scitex.ui` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/utils.rst.txt b/src/scitex/_sphinx_html/_sources/modules/utils.rst.txt new file mode 100644 index 000000000..270e2757f --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/utils.rst.txt @@ -0,0 +1,4 @@ +utils Module (``stx.utils``) +============================ + +See :doc:`/api/scitex.utils` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/web.rst.txt b/src/scitex/_sphinx_html/_sources/modules/web.rst.txt new file mode 100644 index 000000000..cb407ca28 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/web.rst.txt @@ -0,0 +1,4 @@ +web Module (``stx.web``) +======================== + +See :doc:`/api/scitex.web` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/modules/writer.rst.txt b/src/scitex/_sphinx_html/_sources/modules/writer.rst.txt new file mode 100644 index 000000000..4f24d75a2 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/modules/writer.rst.txt @@ -0,0 +1,164 @@ +Writer Module (``stx.writer``) +============================== + +LaTeX manuscript compilation, figure/table management, bibliography +handling, and IMRAD writing guidelines. + +.. note:: + + ``stx.writer`` wraps the standalone + `scitex-writer `_ package. + Install with: ``pip install scitex-writer``. + +Quick Reference +--------------- + +.. code-block:: python + + from scitex.writer import Writer + from pathlib import Path + + writer = Writer(Path("my_paper")) + + # Compile manuscript → PDF + result = writer.compile_manuscript() + if result.success: + print(f"PDF: {result.output_pdf}") + + # Compile supplementary materials + result = writer.compile_supplementary() + + # Compile revision with change tracking + result = writer.compile_revision(track_changes=True) + +CLI Usage +--------- + +.. code-block:: bash + + # Compile + scitex writer compile manuscript ./my-paper + scitex writer compile supplementary ./my-paper + scitex writer compile revision ./my-paper --track-changes + + # Bibliography + scitex writer bib list ./my-paper + scitex writer bib add ./my-paper "@article{...}" + + # Tables and figures + scitex writer tables add ./my-paper data.csv + scitex writer figures list ./my-paper + + # Writing guidelines + scitex writer guidelines get abstract + +Project Structure +----------------- + +A writer project follows this layout: + +.. code-block:: text + + my_paper/ + +-- 00_shared/ + | +-- bibliography.bib + | +-- figures/ + | +-- tables/ + +-- 01_manuscript/ + | +-- main.tex + | +-- sections/ + +-- 02_supplementary/ + | +-- main.tex + +-- 03_revision/ + +-- main.tex + +Create a new project: + +.. code-block:: bash + + scitex template clone paper my_paper + +Key Classes +----------- + +- ``Writer`` -- Main entry point for compilation and project management +- ``CompilationResult`` -- Compilation outcome (success, output path, logs) +- ``ManuscriptTree`` -- Document tree for the main manuscript +- ``SupplementaryTree`` -- Document tree for supplementary materials +- ``RevisionTree`` -- Document tree for revision responses + +Document Management +------------------- + +**Figures:** + +.. code-block:: python + + # Add figure (copies image + creates caption file) + writer.add_figure("fig1", "plot.png", caption="Results") + + # List figures + writer.list_figures() + + # Convert between formats + writer.convert_figure("figure.pdf", "figure.png", dpi=300) + +**Tables:** + +.. code-block:: python + + # Add table from CSV + writer.add_table("tab1", csv_content, caption="Demographics") + + # CSV ↔ LaTeX conversion + writer.csv_to_latex("data.csv", "table.tex") + writer.latex_to_csv("table.tex", "data.csv") + +**Bibliography:** + +.. code-block:: python + + # Add BibTeX entry + writer.add_bibentry('@article{key, title={...}, ...}') + + # Merge multiple .bib files + writer.merge_bibfiles(output_file="bibliography.bib") + +Writing Guidelines +------------------ + +IMRAD guidelines for each manuscript section: + +.. code-block:: bash + + scitex writer guidelines list + scitex writer guidelines get introduction + +.. code-block:: python + + from scitex.writer import guidelines + + # Get guideline for a section + guide = guidelines.get("methods") + + # Build editing prompt: guideline + draft + prompt = guidelines.build("discussion", draft_text) + +Compilation Options +------------------- + +.. code-block:: python + + result = writer.compile_manuscript( + timeout=300, # Max compilation time (seconds) + no_figs=False, # Exclude figures + no_tables=False, # Exclude tables + draft=False, # Draft mode (fast, lower quality) + dark_mode=False, # Dark color scheme + quiet=False, # Suppress output + ) + +API Reference +------------- + +See :doc:`/api/scitex.writer` for the auto-generated Python API. diff --git a/src/scitex/_sphinx_html/_sources/quickstart.rst.txt b/src/scitex/_sphinx_html/_sources/quickstart.rst.txt new file mode 100644 index 000000000..5f94fba36 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/quickstart.rst.txt @@ -0,0 +1,231 @@ +Quick Start +=========== + +Three Interfaces +---------------- + +SciTeX exposes the same capabilities through three interfaces: +Python API, CLI commands, and MCP tools for AI agents. + +Python API +^^^^^^^^^^ + +``@stx.session`` -- Reproducible Experiment Tracking + +.. code-block:: python + + import scitex as stx + + @stx.session + def main(filename="demo.jpg"): + fig, ax = stx.plt.subplots() + ax.plot_line(t, signal) + ax.set_xyt("Time (s)", "Amplitude", "Title") + stx.io.save(fig, filename) + return 0 + +Output: + +.. code-block:: text + + script_out/FINISHED_SUCCESS/2025-01-08_12-30-00_AbC1/ + |-- demo.jpg # Figure with embedded metadata + |-- demo.csv # Auto-exported plot data + |-- CONFIGS/CONFIG.yaml # Reproducible parameters + +-- logs/{stdout,stderr}.log # Execution logs + +``stx.io`` -- Universal File I/O (30+ formats) + +.. code-block:: python + + stx.io.save(df, "output.csv") + stx.io.save(fig, "output.jpg") + df = stx.io.load("output.csv") + +``stx.stats`` -- Publication-Ready Statistics (23 tests) + +.. code-block:: python + + result = stx.stats.test_ttest_ind(group1, group2, return_as="dataframe") + # Includes: p-value, effect size, CI, normality check, power + +``stx.plt`` -- Publication-Ready Figures + +.. code-block:: python + + fig, ax = stx.plt.subplots() + ax.plot_line(x, y, label="signal") + ax.set_xyt("Time (s)", "Amplitude", "Sine Wave") + stx.io.save(fig, "plot.png") + # Creates: plot.png + plot.csv + plot.yaml (recipe for reproduction) + +``stx.scholar`` -- Literature Management + +.. code-block:: python + + # Enrich BibTeX with abstracts, DOIs, citation counts + stx.scholar.enrich_bibtex("refs.bib", add_abstracts=True) + +CLI Commands +^^^^^^^^^^^^ + +.. code-block:: bash + + scitex --help-recursive # Show all commands + scitex scholar fetch "10.1038/..." # Download paper by DOI + scitex scholar bibtex refs.bib # Enrich BibTeX + scitex stats recommend # Suggest statistical tests + scitex audio speak "Done" # Text-to-speech + scitex capture snap # Screenshot + +MCP Tools (120+ tools for AI agents) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Turn AI agents into autonomous scientific researchers. + +.. list-table:: + :header-rows: 1 + :widths: 20 10 70 + + * - Category + - Tools + - Description + * - writer + - 28 + - LaTeX manuscript compilation + * - scholar + - 23 + - PDF download, metadata enrichment + * - capture + - 12 + - Screen monitoring and capture + * - introspect + - 12 + - Python code introspection + * - audio + - 10 + - Text-to-speech, audio playback + * - stats + - 10 + - Automated statistical testing + * - plt + - 9 + - Matplotlib figure creation + * - diagram + - 9 + - Mermaid and Graphviz diagrams + * - dataset + - 8 + - Neuroimaging/physiology datasets + * - social + - 7 + - Social media posting + * - canvas + - 7 + - Figure composition + * - template + - 6 + - Project scaffolding + * - verify + - 6 + - Reproducibility verification + * - dev + - 6 + - Version checking + * - ui + - 5 + - Notifications + * - linter + - 3 + - Code pattern checking + +**Claude Code Setup** — add ``.mcp.json`` to your project root. Use ``SCITEX_ENV_SRC`` +to load all configuration from a ``.src`` file — this keeps ``.mcp.json`` static +across environments: + +.. code-block:: json + + { + "mcpServers": { + "scitex": { + "command": "scitex", + "args": ["mcp", "start"], + "env": { + "SCITEX_ENV_SRC": "${SCITEX_ENV_SRC}" + } + } + } + } + +Switch environments via your shell profile: + +.. code-block:: bash + + # Local machine + export SCITEX_ENV_SRC=~/.scitex/scitex/local.src + + # Remote server + export SCITEX_ENV_SRC=~/.scitex/scitex/remote.src + +Generate a template ``.src`` file: + +.. code-block:: bash + + scitex env-template -o ~/.scitex/scitex/local.src + +Or install globally: + +.. code-block:: bash + + scitex mcp install + +Complete Example +---------------- + +A typical research script combining session, I/O, plotting, and statistics: + +.. code-block:: python + + import scitex as stx + + @stx.session + def main( + n=200, + noise=0.1, + CONFIG=stx.INJECTED, + plt=stx.INJECTED, + logger=stx.INJECTED, + ): + """Analyze noisy sine wave.""" + import numpy as np + + # Generate data + x = np.linspace(0, 4 * np.pi, n) + y_clean = np.sin(x) + y_noisy = y_clean + np.random.normal(0, noise, n) + + # Save raw data + stx.io.save({"x": x, "y_clean": y_clean, "y_noisy": y_noisy}, "data.npz") + + # Visualize + fig, ax = plt.subplots() + ax.plot_scatter(x, y_noisy, alpha=0.3, label="Noisy") + ax.plot_line(x, y_clean, color="red", label="True") + ax.set_xyt("x", "y", f"Sine Wave (noise={noise})") + stx.io.save(fig, "sine.png") + + # Correlation between noisy and clean + result = stx.stats.test_pearson(y_clean, y_noisy) + logger.info(f"Pearson r={result['effect_size']:.3f}, p={result['pvalue']:.1e}") + + # Save summary + stx.io.save({"r": result["effect_size"], "p": result["pvalue"]}, "stats.yaml") + + return 0 + +Next Steps +---------- + +- :doc:`core_concepts` -- Architecture and design principles +- :doc:`modules/index` -- Detailed module documentation +- :doc:`gallery` -- Plot examples diff --git a/src/scitex/_sphinx_html/_sources/source/api/ai.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/ai.rst.txt new file mode 100644 index 000000000..e5fb03cee --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/ai.rst.txt @@ -0,0 +1,82 @@ +AI Module (``stx.ai``) +====================== + +Machine learning utilities for training, classification, and metrics +with PyTorch and scikit-learn. + +Quick Reference +--------------- + +.. code-block:: python + + import scitex as stx + + # Training utilities + from scitex.ai import LearningCurveLogger, EarlyStopping + + logger = LearningCurveLogger() + stopper = EarlyStopping(patience=10, direction="minimize") + + for epoch in range(100): + # ... training loop ... + logger({"loss": loss, "acc": acc}, step="Training") + if stopper(val_loss, {"model": model_path}, epoch): + break + + logger.plot_learning_curves(spath="curves.png") + + # Classification + from scitex.ai import ClassificationReporter, Classifier + + clf = Classifier()("SVC") + reporter = ClassificationReporter(output_dir="./results") + reporter.calculate_metrics(y_true, y_pred, y_proba) + reporter.save_summary() + +Training +-------- + +- ``LearningCurveLogger`` -- Track and visualize training/validation/test metrics across epochs +- ``EarlyStopping`` -- Monitor validation metrics and stop when improvement plateaus + +Classification +-------------- + +- ``ClassificationReporter`` -- Unified reporter for single/multi-task classification (balanced accuracy, MCC, ROC-AUC, confusion matrices) +- ``Classifier`` -- Factory for scikit-learn classifiers (SVC, KNN, Logistic Regression, AdaBoost, ...) +- ``CrossValidationExperiment`` -- Cross-validation framework + +Metrics +------- + +Standardized ``calc_*`` functions: + +- ``calc_bacc`` -- Balanced accuracy +- ``calc_mcc`` -- Matthews Correlation Coefficient +- ``calc_conf_mat`` -- Confusion matrix +- ``calc_roc_auc`` -- ROC-AUC score +- ``calc_pre_rec_auc`` -- Precision-Recall AUC +- ``calc_feature_importance`` -- Feature importance scores + +Visualization +------------- + +- ``plot_learning_curve`` -- Training/validation curves +- ``stx_conf_mat`` -- Confusion matrix heatmap +- ``plot_roc_curve`` -- ROC curve +- ``plot_pre_rec_curve`` -- Precision-Recall curve +- ``plot_feature_importance`` -- Feature importance bar plots + +Other +----- + +- ``MultiTaskLoss`` -- Multi-task learning loss weighting +- ``get_optimizer`` / ``set_optimizer`` -- Optimizer management +- ``GenAI`` -- Generative AI wrapper (lazy-loaded) +- Clustering: ``pca``, ``umap`` + +API Reference +------------- + +.. automodule:: scitex.ai + :members: diff --git a/src/scitex/_sphinx_html/_sources/source/api/app.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/app.rst.txt new file mode 100644 index 000000000..18161dba2 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/app.rst.txt @@ -0,0 +1,101 @@ +App Module (``stx.app``) +======================== + +Runtime SDK for SciTeX applications. Provides a unified interface for +file storage, configuration, and lifecycle management that works +identically in local and cloud environments. + +.. note:: + + ``stx.app`` delegates to the standalone + `scitex-app `_ package. + Install with: ``pip install scitex-app``. + +Overview +-------- + +SciTeX applications are self-contained scientific tools that can run +locally or on the SciTeX cloud platform. The ``stx.app`` module +provides the runtime SDK that each application uses to interact with +its environment. + +Quick Start +----------- + +.. code-block:: python + + import scitex as stx + + # Get current application info + info = stx.app.get_info() + + # Access application preferences + prefs = stx.app.get_prefs() + + # Check dependencies + stx.app.check_deps() + +Key Features +------------ + +**Unified File Storage** + Read and write files through a single API that abstracts local + filesystem and cloud storage. + + .. code-block:: python + + # These work identically locally and in the cloud + stx.app.write_file("results/output.csv", data) + content = stx.app.read_file("config/settings.yaml") + +**Configuration Management** + Application preferences are stored in a standard location and + accessible via dot-notation. + + .. code-block:: python + + prefs = stx.app.get_prefs() + print(prefs.theme) + print(prefs.default_format) + + stx.app.set_prefs(theme="dark", default_format="pdf") + +**Application Lifecycle** + Query and manage the running application. + + .. code-block:: python + + info = stx.app.get_info() + print(info.name) + print(info.version) + + current = stx.app.get_current() # Currently active app + +**Dependency Checking** + Verify that all required packages are available. + + .. code-block:: python + + missing = stx.app.check_deps() + if missing: + print(f"Missing: {missing}") + +Creating an Application +----------------------- + +Scaffold a new SciTeX application from a template: + +.. code-block:: bash + + scitex template clone app my_tool + +This creates a project with bridge-init configuration, MountPoint +definitions, and EventBus integration pre-configured. + +API Reference +------------- + +.. automodule:: scitex.app + :members: + :no-undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/audio.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/audio.rst.txt new file mode 100644 index 000000000..bc0a211a2 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/audio.rst.txt @@ -0,0 +1,78 @@ +Audio Module (``stx.audio``) +============================ + +Text-to-speech synthesis and audio file playback. Supports multiple +TTS backends with automatic fallback. + +Quick Start +----------- + +.. code-block:: python + + import scitex as stx + + # Text-to-speech + stx.audio.speak("Analysis complete. 42 significant results found.") + + # Play an audio file + stx.audio.play("notification.wav") + +Key Functions +------------- + +``speak(text, backend=None, **kwargs)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Convert text to speech and play it through the default audio output. +If no backend is specified, the first available backend is used. + +.. code-block:: python + + # Use default backend + stx.audio.speak("Hello from SciTeX") + + # Specify a backend + stx.audio.speak("Hello", backend="espeak") + +``play(path)`` +^^^^^^^^^^^^^^ + +Play an audio file (WAV, MP3, etc.). + +.. code-block:: python + + stx.audio.play("alert.wav") + stx.audio.play("recording.mp3") + +``list_backends()`` +^^^^^^^^^^^^^^^^^^^ + +List available TTS backends on the current system. + +.. code-block:: python + + backends = stx.audio.list_backends() + # e.g., ['espeak', 'festival', 'pyttsx3'] + +Use Cases +--------- + +Audible notifications are useful for long-running experiments: + +.. code-block:: python + + import scitex as stx + + @stx.session + def main(CONFIG=stx.INJECTED): + result = train_model() # Takes hours + stx.audio.speak(f"Training done. Accuracy: {result.accuracy:.1%}") + return 0 + +API Reference +------------- + +.. automodule:: scitex.audio + :members: + :no-undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/audit.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/audit.rst.txt new file mode 100644 index 000000000..e7f92881e --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/audit.rst.txt @@ -0,0 +1,7 @@ +audit Module (``stx.audit``) +============================ + +.. automodule:: scitex.audit + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/benchmark.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/benchmark.rst.txt new file mode 100644 index 000000000..014b80120 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/benchmark.rst.txt @@ -0,0 +1,7 @@ +benchmark Module (``stx.benchmark``) +==================================== + +.. automodule:: scitex.benchmark + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/bridge.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/bridge.rst.txt new file mode 100644 index 000000000..8028c570b --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/bridge.rst.txt @@ -0,0 +1,7 @@ +bridge Module (``stx.bridge``) +============================== + +.. automodule:: scitex.bridge + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/browser.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/browser.rst.txt new file mode 100644 index 000000000..c91604cc8 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/browser.rst.txt @@ -0,0 +1,7 @@ +browser Module (``stx.browser``) +================================ + +.. automodule:: scitex.browser + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/canvas.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/canvas.rst.txt new file mode 100644 index 000000000..210c63590 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/canvas.rst.txt @@ -0,0 +1,7 @@ +canvas Module (``stx.canvas``) +============================== + +.. automodule:: scitex.canvas + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/capture.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/capture.rst.txt new file mode 100644 index 000000000..68c87a179 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/capture.rst.txt @@ -0,0 +1,81 @@ +Capture Module (``stx.capture``) +================================= + +Screenshot capture and screen monitoring utilities. Useful for +documenting GUI states, recording experiment progress, and +automated visual testing. + +Quick Start +----------- + +.. code-block:: python + + import scitex as stx + + # Take a screenshot + img = stx.capture.snap() + + # Save it + stx.io.save(img, "screenshot.png") + +Key Functions +------------- + +``snap(region=None)`` +^^^^^^^^^^^^^^^^^^^^^ + +Capture a screenshot of the entire screen or a specific region. + +.. code-block:: python + + # Full screen + img = stx.capture.snap() + + # Specific region (x, y, width, height) + img = stx.capture.snap(region=(0, 0, 800, 600)) + +``monitor(interval=1.0, duration=None, output_dir=".")`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Capture screenshots at regular intervals. Useful for monitoring +long-running GUI processes. + +.. code-block:: python + + # Capture every 5 seconds for 1 minute + stx.capture.monitor(interval=5.0, duration=60, output_dir="./captures") + +``crop(image, region)`` +^^^^^^^^^^^^^^^^^^^^^^^ + +Crop a captured image to a specific region. + +.. code-block:: python + + img = stx.capture.snap() + cropped = stx.capture.crop(img, region=(100, 100, 400, 300)) + stx.io.save(cropped, "cropped.png") + +Use Cases +--------- + +Documenting experiment results displayed in a GUI: + +.. code-block:: python + + import scitex as stx + + @stx.session + def main(CONFIG=stx.INJECTED): + run_experiment_gui() + img = stx.capture.snap() + stx.io.save(img, "final_state.png") + return 0 + +API Reference +------------- + +.. automodule:: scitex.capture + :members: + :no-undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/clew.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/clew.rst.txt new file mode 100644 index 000000000..017427e08 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/clew.rst.txt @@ -0,0 +1,94 @@ +Clew Module (``stx.clew``) +========================== + +Hash-based provenance tracking for reproducible science. Clew (Ariadne's +thread) records file hashes during ``@stx.session`` runs and traces +dependency chains back to source. + +How It Works +------------ + +1. ``@stx.session`` starts a tracking session +2. ``stx.io.load()`` records input file hashes +3. ``stx.io.save()`` records output file hashes +4. Session close computes a combined hash of all inputs/outputs +5. Later, ``stx.clew`` can verify nothing has changed + +.. code-block:: python + + import scitex as stx + + # Automatic -- just use @stx.session + stx.io + @stx.session + def main(): + data = stx.io.load("input.csv") # Tracked as input + result = process(data) + stx.io.save(result, "output.png") # Tracked as output + return 0 + + # Verify later + stx.clew.status() # Like git status + stx.clew.run("session_id") # Verify by hash + stx.clew.chain("output.png") # Trace to source + +CLI Commands +------------ + +.. code-block:: bash + + scitex clew status # Show changed files + scitex clew list # List all tracked runs + scitex clew run # Verify a specific run + scitex clew chain # Trace dependency chain + scitex clew stats # Database statistics + +Verification Levels +------------------- + +- **CACHE** -- Hash comparison only (fast). Checks if files match stored hashes. +- **RERUN** -- Re-execute scripts and compare outputs (thorough). Catches logic errors. + +.. code-block:: python + + # Fast: hash comparison + result = stx.clew.run("session_id") + + # Thorough: re-execute and compare + result = stx.clew.run("session_id", from_scratch=True) + +Dependency Chains +----------------- + +Clew traces ``parent_session`` links to build a DAG from final output +back to original source: + +.. code-block:: python + + chain = stx.clew.chain("final_figure.png") + # Shows: source.py → intermediate.csv → analysis.py → final_figure.png + + # Visualize as Mermaid DAG + stx.clew.mermaid("session_id") + +Verification Statuses +--------------------- + +- ``VERIFIED`` -- Files match expected hashes +- ``MISMATCH`` -- Files differ from stored hashes +- ``MISSING`` -- Files no longer exist +- ``UNKNOWN`` -- No prior tracking data + +Key Functions +------------- + +- ``status()`` -- Show changed items (like ``git status``) +- ``run(session_id)`` -- Verify a specific run +- ``chain(target_file)`` -- Trace dependency chain +- ``list_runs(limit, status)`` -- List tracked runs +- ``stats()`` -- Database statistics + +API Reference +------------- + +.. automodule:: scitex.clew + :members: diff --git a/src/scitex/_sphinx_html/_sources/source/api/cli.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/cli.rst.txt new file mode 100644 index 000000000..c6054e8fd --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/cli.rst.txt @@ -0,0 +1,7 @@ +cli Module (``stx.cli``) +======================== + +.. automodule:: scitex.cli + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/cloud.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/cloud.rst.txt new file mode 100644 index 000000000..6c265efe4 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/cloud.rst.txt @@ -0,0 +1,123 @@ +Cloud Module (``stx.cloud``) +============================ + +Cloud platform integration for the SciTeX ecosystem. Provides +programmatic access to remote execution, file storage, job management, +and repository operations. + +Overview +-------- + +The cloud module connects local SciTeX workflows to the cloud platform, +enabling remote computation, shared storage, and collaborative +features. All operations are available through Python API, CLI, and +MCP tools. + +Quick Start +----------- + +.. code-block:: python + + import scitex as stx + + # Check cloud connection + status = stx.cloud.status() + + # Submit a job + job_id = stx.cloud.jobs.submit(script="train.py", resources={"gpu": 1}) + + # Check job status + stx.cloud.jobs.status(job_id) + +Key Features +------------ + +Job Management +^^^^^^^^^^^^^^ + +Submit, monitor, and retrieve results from remote compute jobs. + +.. code-block:: python + + # Submit a job + job_id = stx.cloud.jobs.submit( + script="train.py", + resources={"gpu": 1, "memory": "16G"}, + ) + + # Poll status + status = stx.cloud.jobs.status(job_id) + + # List all jobs + stx.cloud.jobs.list() + + # Cancel a running job + stx.cloud.jobs.cancel(job_id) + +File Storage +^^^^^^^^^^^^ + +Upload and download files to/from cloud storage. + +.. code-block:: python + + # Upload a file + stx.cloud.files.upload("local_data.h5", remote="data/experiment.h5") + + # Download a file + stx.cloud.files.download("data/experiment.h5", local="./downloaded.h5") + + # List remote files + stx.cloud.files.list("data/") + +Data API +^^^^^^^^ + +CRUD operations on structured data stored in the cloud. + +.. code-block:: python + + # Create a record + stx.cloud.data.create(collection="results", data={"accuracy": 0.95}) + + # Search records + results = stx.cloud.data.search(collection="results", query={"accuracy": {"$gt": 0.9}}) + + # List collections + stx.cloud.data.list() + +Repository Operations +^^^^^^^^^^^^^^^^^^^^^ + +Manage git repositories through the cloud API. + +.. code-block:: python + + stx.cloud.repo.list() + stx.cloud.repo.status("my-project") + stx.cloud.repo.push("my-project") + +CLI Access +---------- + +.. code-block:: bash + + # Job management + scitex cloud jobs submit train.py --gpu 1 + scitex cloud jobs status JOB_ID + scitex cloud jobs list + + # File operations + scitex cloud files upload data.h5 + scitex cloud files list + + # Status + scitex cloud status + +API Reference +------------- + +.. automodule:: scitex.cloud + :members: + :no-undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/compat.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/compat.rst.txt new file mode 100644 index 000000000..7438ae6ca --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/compat.rst.txt @@ -0,0 +1,7 @@ +compat Module (``stx.compat``) +============================== + +.. automodule:: scitex.compat + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/config.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/config.rst.txt new file mode 100644 index 000000000..11a770d0b --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/config.rst.txt @@ -0,0 +1,88 @@ +Config Module (``stx.config``) +=============================== + +YAML-based configuration with priority resolution and path management. + +Priority Resolution +------------------- + +All config values follow the same precedence: + +.. code-block:: text + + direct argument > config/YAML > environment variable > default + +Quick Reference +--------------- + +.. code-block:: python + + import scitex as stx + + # Get global config (reads ~/.scitex/config.yaml) + config = stx.config.get_config() + + # Resolve with precedence + log_level = config.resolve("logging.level", default="INFO") + # Checks: direct → YAML → SCITEX_LOGGING_LEVEL env → "INFO" + + # Access nested values + config.get_nested("scholar", "crossref_email") + + # Path management + paths = stx.config.get_paths() + paths.scholar # ~/.scitex/scholar + paths.cache # ~/.scitex/cache + +YAML Configuration +------------------ + +.. code-block:: yaml + + # ~/.scitex/config.yaml (or ./config/*.yaml for projects) + logging: + level: INFO + + scholar: + crossref_email: ${CROSSREF_EMAIL:-user@example.com} + +Environment variable substitution with ``${VAR:-default}`` syntax is +supported in YAML files. + +Key Classes +----------- + +- ``ScitexConfig`` -- YAML-based config with env var substitution and precedence resolution +- ``PriorityConfig`` -- Dict-based config resolver for programmatic use +- ``ScitexPaths`` -- Centralized path manager (all paths derive from ``~/.scitex/``) +- ``EnvVar`` -- Environment variable definition dataclass + +Path Management +--------------- + +``ScitexPaths`` manages all SciTeX directories: + +.. code-block:: python + + paths = stx.config.get_paths() + paths.base # ~/.scitex (SCITEX_DIR) + paths.logs # ~/.scitex/logs + paths.cache # ~/.scitex/cache + paths.scholar # ~/.scitex/scholar + paths.rng # ~/.scitex/rng + paths.capture # ~/.scitex/capture + +Utility Functions +----------------- + +- ``get_config(path)`` -- Get singleton ScitexConfig instance +- ``get_paths(base_dir)`` -- Get singleton ScitexPaths instance +- ``load_yaml(path)`` -- Load YAML with env var substitution +- ``load_dotenv(path)`` -- Load ``.env`` file +- ``get_scitex_dir()`` -- Get SCITEX_DIR with precedence + +API Reference +------------- + +.. automodule:: scitex.config + :members: diff --git a/src/scitex/_sphinx_html/_sources/source/api/container.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/container.rst.txt new file mode 100644 index 000000000..6ac42573b --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/container.rst.txt @@ -0,0 +1,7 @@ +container Module (``stx.container``) +==================================== + +.. automodule:: scitex.container + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/context.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/context.rst.txt new file mode 100644 index 000000000..7a7f5b8ff --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/context.rst.txt @@ -0,0 +1,7 @@ +context Module (``stx.context``) +================================ + +.. automodule:: scitex.context + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/cv.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/cv.rst.txt new file mode 100644 index 000000000..e4b53ed96 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/cv.rst.txt @@ -0,0 +1,7 @@ +cv Module (``stx.cv``) +====================== + +.. automodule:: scitex.cv + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/dataset.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/dataset.rst.txt new file mode 100644 index 000000000..3ceca7e6d --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/dataset.rst.txt @@ -0,0 +1,125 @@ +Dataset Module (``stx.dataset``) +================================= + +Unified access to public scientific datasets from major neuroscience +and biomedical repositories. Search, browse, and fetch datasets +without learning each repository's API. + +Supported Sources +----------------- + +.. list-table:: + :header-rows: 1 + :widths: 25 75 + + * - Source + - Description + * - `DANDI `_ + - Neurophysiology data (NWB format) -- electrophysiology, calcium imaging + * - `OpenNeuro `_ + - Brain imaging data (BIDS format) -- fMRI, EEG, MEG, PET + * - `PhysioNet `_ + - Physiological signal data -- ECG, EEG, clinical waveforms + +Quick Start +----------- + +.. code-block:: python + + import scitex as stx + + # Search across all sources + results = stx.dataset.search("epilepsy EEG") + + # Fetch a specific dataset + data = stx.dataset.fetch("dandi", dandiset_id="000003") + + # List available sources + sources = stx.dataset.list_sources() + # ['dandi', 'openneuro', 'physionet'] + +Key Functions +------------- + +``search(query, source=None)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Search for datasets across one or all supported repositories. + +.. code-block:: python + + # Search all sources + results = stx.dataset.search("motor imagery BCI") + + # Search a specific source + results = stx.dataset.search("sleep staging", source="physionet") + + for r in results: + print(f"{r.source}: {r.title} ({r.id})") + +``fetch(source, **kwargs)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Download or stream a dataset from a specific source. + +.. code-block:: python + + # Fetch from DANDI + data = stx.dataset.fetch("dandi", dandiset_id="000003") + + # Fetch from OpenNeuro + data = stx.dataset.fetch("openneuro", dataset_id="ds000117") + + # Fetch from PhysioNet + data = stx.dataset.fetch("physionet", database="mimic-iv", version="2.0") + +``list_sources()`` +^^^^^^^^^^^^^^^^^^ + +List all available dataset sources and their status. + +.. code-block:: python + + sources = stx.dataset.list_sources() + # ['dandi', 'openneuro', 'physionet'] + +Local Database +-------------- + +Build a local search index for faster repeated queries: + +.. code-block:: python + + # Build/update the local database + stx.dataset.db_build(sources=["dandi", "openneuro"]) + + # Search the local index (fast, offline) + results = stx.dataset.db_search("resting state fMRI") + + # Get database statistics + stx.dataset.db_stats() + +CLI Access +---------- + +.. code-block:: bash + + # Search datasets + scitex dataset search "epilepsy EEG" + + # Fetch a dataset + scitex dataset fetch dandi --dandiset-id 000003 + + # List sources + scitex dataset list-sources + + # Build local database + scitex dataset db-build + +API Reference +------------- + +.. automodule:: scitex.dataset + :members: + :no-undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/datetime.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/datetime.rst.txt new file mode 100644 index 000000000..78541bb81 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/datetime.rst.txt @@ -0,0 +1,7 @@ +datetime Module (``stx.datetime``) +================================== + +.. automodule:: scitex.datetime + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/db.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/db.rst.txt new file mode 100644 index 000000000..5945aa66d --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/db.rst.txt @@ -0,0 +1,7 @@ +db Module (``stx.db``) +====================== + +.. automodule:: scitex.db + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/decorators.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/decorators.rst.txt new file mode 100644 index 000000000..b1a5f2fd0 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/decorators.rst.txt @@ -0,0 +1,80 @@ +Decorators Module (``stx.decorators``) +======================================= + +Function decorators for type conversion, batching, caching, and more. + +Quick Reference +--------------- + +.. code-block:: python + + from scitex.decorators import torch_fn, batch_fn, cache_disk, timeout + + @torch_fn + def process(x): + """Auto-converts inputs to torch tensors, outputs back.""" + return x * 2 + + @batch_fn + def predict(data, batch_size=32): + """Process data in batches with progress bar.""" + return model(data) + + @cache_disk + def expensive_computation(params): + """Results cached to disk for reuse.""" + return heavy_compute(params) + + @timeout(seconds=30) + def risky_call(): + """Raises TimeoutError if exceeds 30s.""" + return external_api() + +Type Conversion +--------------- + +Auto-convert between data frameworks: + +- ``@torch_fn`` -- Inputs to PyTorch tensors, outputs back to original type +- ``@numpy_fn`` -- Inputs to NumPy arrays +- ``@pandas_fn`` -- Inputs to pandas objects +- ``@xarray_fn`` -- Inputs to xarray objects + +Batch Processing +---------------- + +- ``@batch_fn`` -- Split input into batches with tqdm progress +- ``@batch_numpy_fn`` -- NumPy conversion + batching +- ``@batch_torch_fn`` -- PyTorch conversion + batching +- ``@batch_pandas_fn`` -- Pandas conversion + batching + +Caching +------- + +- ``@cache_mem`` -- In-memory function result caching +- ``@cache_disk`` -- Persistent disk-based caching +- ``@cache_disk_async`` -- Async disk caching + +Utilities +--------- + +- ``@deprecated(reason, forward_to)`` -- Mark functions as deprecated +- ``@not_implemented`` -- Mark as not yet implemented +- ``@timeout(seconds)`` -- Enforce execution time limits +- ``@preserve_doc`` -- Preserve docstrings when wrapping + +Auto-Ordering +------------- + +.. code-block:: python + + from scitex.decorators import enable_auto_order + + enable_auto_order() + # Now decorators are applied in optimal order regardless of code order + +API Reference +------------- + +.. automodule:: scitex.decorators + :members: diff --git a/src/scitex/_sphinx_html/_sources/source/api/dev.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/dev.rst.txt new file mode 100644 index 000000000..63e389f2f --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/dev.rst.txt @@ -0,0 +1,163 @@ +Dev Module (``stx.dev``) +======================== + +Development tools and ecosystem management for the SciTeX package family. + +.. note:: + + ``stx.dev`` delegates to the standalone + `scitex-dev `_ package. + Install with: ``pip install scitex-dev``. + +Overview +-------- + +The dev module provides utilities for maintaining the SciTeX ecosystem of +packages, building documentation, managing versions, and creating +MCP/CLI wrappers for scientific tools. It is primarily used by package +maintainers and contributors rather than end users. + +Ecosystem Management +-------------------- + +The SciTeX ecosystem comprises 14+ packages. ``stx.dev`` provides a +unified registry and coordination tools. + +.. code-block:: python + + import scitex as stx + + # List all ecosystem packages + stx.dev.list_versions() + + # Check version consistency across the ecosystem + stx.dev.check_versions() + + # Synchronize local clones + stx.dev.ecosystem_sync() + + # Commit across multiple packages + stx.dev.ecosystem_commit("fix: update shared constants") + +.. list-table:: Ecosystem Packages + :header-rows: 1 + :widths: 30 70 + + * - Package + - Purpose + * - ``scitex`` + - Hub package (re-exports all modules) + * - ``scitex-io`` + - File I/O for 30+ formats + * - ``scitex-stats`` + - Statistical testing with auto-reporting + * - ``scitex-linter`` + - AST-based convention checker + * - ``scitex-clew`` + - Claim-evidence-workflow pipeline + * - ``figrecipe`` + - Publication-quality plotting + * - ``scitex-dev`` + - Development and ecosystem tools + * - ``scitex-notification`` + - Multi-backend notifications + * - ``scitex-app`` + - Runtime SDK for SciTeX applications + +LLM-friendly Types +------------------- + +``stx.dev`` provides structured return types designed for consumption +by both humans and AI agents: + +.. code-block:: python + + from scitex.dev import Result, ErrorCode + + # Wrap function results for consistent handling + result = Result(ok=True, data={"accuracy": 0.95}) + result = Result(ok=False, error=ErrorCode.NOT_FOUND, message="File missing") + + # Decorator to add return_as= parameter + @stx.dev.supports_return_as + def analyze(data): + return {"mean": data.mean()} + + analyze(data, return_as="json") # JSON string + analyze(data, return_as="dict") # Python dict + +MCP and CLI Wrappers +-------------------- + +Convert Python functions into MCP tools or CLI commands: + +.. code-block:: python + + from scitex.dev import wrap_as_mcp, wrap_as_cli + + def my_tool(path: str, threshold: float = 0.5) -> dict: + """Analyze a data file.""" + ... + + # Register as MCP tool (for AI agent access) + mcp_tool = wrap_as_mcp(my_tool) + + # Register as CLI command (for terminal access) + cli_cmd = wrap_as_cli(my_tool) + +Documentation +------------- + +Build and search unified documentation across the ecosystem: + +.. code-block:: python + + # Build Sphinx docs for a package + stx.dev.build_docs("scitex-io") + + # Get docstring for any public function + stx.dev.get_docs("stx.io.save") + + # Full-text search across all packages + results = stx.dev.search("load_configs") + +Hot Reload +---------- + +Reload modules during interactive development: + +.. code-block:: python + + stx.dev.reload("scitex.io") # Re-import scitex.io from disk + +HPC Testing +----------- + +Submit test suites to HPC clusters and poll results: + +.. code-block:: bash + + scitex dev test-hpc --package scitex-io + scitex dev test-hpc-poll --job-id 12345 + +Bulk Rename +----------- + +Rename symbols across an entire codebase safely: + +.. code-block:: python + + stx.dev.bulk_rename( + root="./src", + old="old_function_name", + new="new_function_name", + dry_run=True, # Preview changes first + ) + +API Reference +------------- + +.. automodule:: scitex.dev + :members: + :no-undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/diagram.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/diagram.rst.txt new file mode 100644 index 000000000..7e3a9f997 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/diagram.rst.txt @@ -0,0 +1,99 @@ +Diagram Module (``stx.diagram``) +================================= + +Paper-optimized diagram generation with Mermaid and Graphviz backends. + +Quick Reference +--------------- + +.. code-block:: python + + from scitex.diagram import Diagram + + # Create from Python + d = Diagram(type="workflow", title="Analysis Pipeline") + d.add_node("load", "Load Data", shape="stadium") + d.add_node("proc", "Process", shape="rounded", emphasis="primary") + d.add_node("save", "Save Results", shape="stadium", emphasis="success") + d.add_edge("load", "proc") + d.add_edge("proc", "save") + + # Export + d.to_mermaid("pipeline.mmd") + d.to_graphviz("pipeline.dot") + + # Or from YAML specification + d = Diagram.from_yaml("pipeline.diagram.yaml") + +YAML Specification +------------------ + +.. code-block:: yaml + + type: workflow + title: SciTeX Analysis Pipeline + + paper: + column: single # single or double + mode: publication # draft or publication + reading_direction: left_to_right + + layout: + groups: + Input: [load, preprocess] + Analysis: [analyze, test] + + nodes: + - id: load + label: Load Data + shape: stadium + - id: analyze + label: Statistical Test + shape: rounded + emphasis: primary + + edges: + - from: load + to: analyze + +Node Shapes +------------ + +``box``, ``rounded``, ``stadium``, ``diamond``, ``circle``, ``codeblock`` + +Node Emphasis +------------- + +- ``normal`` -- Default (dark) +- ``primary`` -- Key nodes (blue) +- ``success`` -- Positive outcomes (green) +- ``warning`` -- Issues (red) +- ``muted`` -- Secondary (gray) + +Diagram Types +------------- + +- ``workflow`` -- Left-to-right sequential processes +- ``decision`` -- Top-to-bottom decision trees +- ``pipeline`` -- Data pipeline stages +- ``hierarchy`` -- Tree structures +- ``comparison`` -- Side-by-side comparison + +Paper Modes +----------- + +- **draft** -- Full arrows, medium spacing, all edges visible +- **publication** -- Tight spacing, return edges invisible, optimized for column width + +Backends +-------- + +- **Mermaid** (``.mmd``) -- Web/markdown rendering +- **Graphviz DOT** (``.dot``) -- Tighter layouts, render with ``dot -Tpng`` +- **YAML** (``.diagram.yaml``) -- Semantic spec, human/LLM-readable + +API Reference +------------- + +.. automodule:: scitex.diagram + :members: diff --git a/src/scitex/_sphinx_html/_sources/source/api/dict.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/dict.rst.txt new file mode 100644 index 000000000..7157aba95 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/dict.rst.txt @@ -0,0 +1,7 @@ +dict Module (``stx.dict``) +========================== + +.. automodule:: scitex.dict + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/dsp.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/dsp.rst.txt new file mode 100644 index 000000000..4e16f6f2d --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/dsp.rst.txt @@ -0,0 +1,75 @@ +DSP Module (``stx.dsp``) +======================== + +Digital signal processing tools for filtering, spectral analysis, +and time-frequency decomposition. + +Quick Reference +--------------- + +.. code-block:: python + + import scitex as stx + import numpy as np + + # Generate test signal + fs = 1000 # Hz + t = np.arange(0, 1, 1/fs) + signal = np.sin(2 * np.pi * 10 * t) + 0.5 * np.sin(2 * np.pi * 50 * t) + + # Filtering + filtered = stx.dsp.filt.bandpass(signal, fs=fs, low=5, high=30) + low_pass = stx.dsp.filt.lowpass(signal, fs=fs, cutoff=20) + high_pass = stx.dsp.filt.highpass(signal, fs=fs, cutoff=5) + + # Power spectral density + freqs, psd = stx.dsp.psd(signal, fs=fs) + + # Band powers + powers = stx.dsp.band_powers(signal, fs=fs, bands={ + "delta": (1, 4), + "theta": (4, 8), + "alpha": (8, 13), + "beta": (13, 30), + }) + + # Hilbert transform (analytic signal) + analytic = stx.dsp.hilbert(signal) + amplitude = np.abs(analytic) + phase = np.angle(analytic) + + # Wavelet decomposition + coeffs = stx.dsp.wavelet(signal, fs=fs, freqs=np.arange(1, 50)) + +Available Functions +------------------- + +**Filtering** (``stx.dsp.filt``) + +- ``bandpass(signal, fs, low, high)`` +- ``lowpass(signal, fs, cutoff)`` +- ``highpass(signal, fs, cutoff)`` +- ``notch(signal, fs, freq)`` + +**Spectral Analysis** + +- ``psd(signal, fs)`` -- Power spectral density +- ``band_powers(signal, fs, bands)`` -- Power in frequency bands +- ``wavelet(signal, fs, freqs)`` -- Continuous wavelet transform + +**Time-Frequency** + +- ``hilbert(signal)`` -- Analytic signal via Hilbert transform +- ``pac(signal, fs)`` -- Phase-amplitude coupling + +**Utilities** + +- ``demo_sig(fs, duration)`` -- Generate demo signals for testing +- ``crop(signal, start, end, fs)`` -- Crop signal to time range +- ``detect_ripples(signal, fs)`` -- Detect sharp-wave ripples + +API Reference +------------- + +.. automodule:: scitex.dsp + :members: diff --git a/src/scitex/_sphinx_html/_sources/source/api/etc.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/etc.rst.txt new file mode 100644 index 000000000..b10d3a1f4 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/etc.rst.txt @@ -0,0 +1,7 @@ +etc Module (``stx.etc``) +======================== + +.. automodule:: scitex.etc + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/events.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/events.rst.txt new file mode 100644 index 000000000..2039cfecd --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/events.rst.txt @@ -0,0 +1,7 @@ +events Module (``stx.events``) +============================== + +.. automodule:: scitex.events + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/gen.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/gen.rst.txt new file mode 100644 index 000000000..4a982c9fc --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/gen.rst.txt @@ -0,0 +1,38 @@ +Gen Module (``stx.gen``) +======================== + +General utilities for path management, shell commands, and system operations. + +.. note:: + + Session management has moved to ``@stx.session`` (see :doc:`/core_concepts`). + The ``scitex.gen.start()`` / ``scitex.gen.close()`` pattern is deprecated. + +Quick Reference +--------------- + +.. code-block:: python + + import scitex as stx + + # Timestamped output directories + path = stx.gen.mk_spath("./results") + # → ./results/20260213_143022/ + + # Run shell commands + stx.gen.run("ls -la") + + # Clipboard operations + stx.gen.copy("text to clipboard") + text = stx.gen.paste() + + # String to valid path + stx.gen.title2path("My Experiment #1") + # → "my_experiment_1" + +API Reference +------------- + +.. automodule:: scitex.gen + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/gists.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/gists.rst.txt new file mode 100644 index 000000000..b77c7eaf9 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/gists.rst.txt @@ -0,0 +1,7 @@ +gists Module (``stx.gists``) +============================ + +.. automodule:: scitex.gists + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/git.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/git.rst.txt new file mode 100644 index 000000000..fcec156e3 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/git.rst.txt @@ -0,0 +1,7 @@ +git Module (``stx.git``) +======================== + +.. automodule:: scitex.git + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/index.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/index.rst.txt new file mode 100644 index 000000000..50f15de62 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/index.rst.txt @@ -0,0 +1,118 @@ +API Reference +============= + +SciTeX is organized into focused modules. +All modules are accessible via ``import scitex as stx`` followed by ``stx.``. + +.. toctree:: + :maxdepth: 2 + :caption: Core + + session + io + config + logging + repro + clew + +.. toctree:: + :maxdepth: 2 + :caption: Science & Analysis + + stats + plt + dsp + diagram + canvas + +.. toctree:: + :maxdepth: 2 + :caption: Literature & Writing + + scholar + writer + notebook + +.. toctree:: + :maxdepth: 2 + :caption: Machine Learning + + ai + nn + torch + cv + benchmark + +.. toctree:: + :maxdepth: 2 + :caption: Data & I/O + + pd + db + dataset + schema + +.. toctree:: + :maxdepth: 2 + :caption: Infrastructure + + app + cloud + container + tunnel + cli + browser + capture + audio + notification + social + +.. toctree:: + :maxdepth: 2 + :caption: Development + + dev + template + linter + introspect + audit + +.. toctree:: + :maxdepth: 2 + :caption: Utilities + + gen + decorators + str + dict + path + os + sh + git + parallel + linalg + datetime + types + rng + context + resource + utils + etc + web + msword + tex + bridge + compat + module + gists + media + security + ui + usage + +.. toctree:: + :maxdepth: 2 + :caption: Other + + events + project diff --git a/src/scitex/_sphinx_html/_sources/source/api/introspect.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/introspect.rst.txt new file mode 100644 index 000000000..180c1dafb --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/introspect.rst.txt @@ -0,0 +1,71 @@ +Introspect Module (``stx.introspect``) +======================================= + +IPython-like code inspection for exploring Python packages. + +Quick Reference +--------------- + +.. code-block:: python + + import scitex as stx + + # Function signature (like IPython's func?) + stx.introspect.q("scitex.stats.test_ttest_ind") + # → name, signature, parameters, return type + + # Full source code (like IPython's func??) + stx.introspect.qq("scitex.stats.test_ttest_ind") + # → complete source with line numbers + + # List module members (like enhanced dir()) + stx.introspect.dir("scitex.plt", filter="public", kind="functions") + + # Recursive API tree + df = stx.introspect.list_api("scitex", max_depth=2) + +IPython-Style Shortcuts +----------------------- + +- ``q(dotted_path)`` -- Signature and parameters (like ``func?``) +- ``qq(dotted_path)`` -- Full source code (like ``func??``) +- ``dir(dotted_path, filter, kind)`` -- List members with filtering + +Filters: ``"public"``, ``"private"``, ``"dunder"``, ``"all"`` + +Kinds: ``"functions"``, ``"classes"``, ``"data"``, ``"modules"`` + +Documentation +------------- + +- ``get_docstring(path, format)`` -- Extract docstrings (``"raw"``, ``"parsed"``, ``"summary"``) +- ``get_exports(path)`` -- Get ``__all__`` exports +- ``find_examples(path)`` -- Find usage examples in tests/examples + +Type Analysis +------------- + +- ``get_type_hints_detailed(path)`` -- Full type annotation analysis +- ``get_class_hierarchy(path)`` -- Inheritance tree (MRO + subclasses) +- ``get_class_annotations(path)`` -- Class variable annotations + +Code Analysis +------------- + +- ``get_imports(path, categorize)`` -- All imports (AST-based, grouped by stdlib/third-party/local) +- ``get_dependencies(path, recursive)`` -- Module dependency tree +- ``get_call_graph(path, max_depth)`` -- Function call graph (with timeout protection) + +API Tree +-------- + +.. code-block:: python + + # Generate full module tree as DataFrame + df = stx.introspect.list_api("scitex", max_depth=3, docstring=True) + +API Reference +------------- + +.. automodule:: scitex.introspect + :members: diff --git a/src/scitex/_sphinx_html/_sources/source/api/io.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/io.rst.txt new file mode 100644 index 000000000..9489168df --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/io.rst.txt @@ -0,0 +1,181 @@ +I/O Module (``stx.io``) +======================== + +Unified file I/O for 30+ scientific data formats. A single ``save()`` +and ``load()`` interface handles format detection by file extension, +so you never need to remember which library reads ``.npy`` vs ``.h5`` +vs ``.parquet``. + +.. note:: + + Core format handlers are provided by the standalone + `scitex-io `_ package. + ``stx.io`` adds integration with ``stx.session`` (provenance tracking), + ``stx.clew`` (claim verification), and automatic CSV export for figures. + +Supported Formats +----------------- + +.. list-table:: + :header-rows: 1 + :widths: 25 75 + + * - Category + - Extensions + * - **Tabular** + - ``.csv``, ``.tsv``, ``.xlsx``, ``.xls``, ``.parquet``, ``.feather`` + * - **Array** + - ``.npy``, ``.npz``, ``.mat``, ``.zarr``, ``.h5`` / ``.hdf5`` + * - **Config** + - ``.yaml``, ``.yml``, ``.json``, ``.toml``, ``.ini``, ``.conf`` + * - **Figure** + - ``.png``, ``.jpg``, ``.jpeg``, ``.svg``, ``.pdf``, ``.tiff`` + * - **Text** + - ``.txt``, ``.log``, ``.md``, ``.rst`` + * - **Audio** + - ``.wav``, ``.mp3``, ``.flac`` + * - **Serialized** + - ``.pkl``, ``.pickle``, ``.joblib`` + * - **Bibliography** + - ``.bib`` + +Quick Start +----------- + +.. code-block:: python + + import scitex as stx + import pandas as pd + import numpy as np + + # --- Save and load a DataFrame --- + df = pd.DataFrame({"x": [1, 2, 3], "y": [4, 5, 6]}) + stx.io.save(df, "results.csv") + df_loaded = stx.io.load("results.csv") + + # --- Save and load a NumPy array --- + arr = np.random.randn(100, 3) + stx.io.save(arr, "data.npy") + arr_loaded = stx.io.load("data.npy") + + # --- Save a figure (PNG + CSV data export) --- + fig, ax = stx.plt.subplots() + ax.stx_line([1, 2, 3, 4, 5], id="my_data") + stx.io.save(fig, "plot.png") + # Creates: plot.png, plot.csv, plot.yaml + +Key Functions +------------- + +``save(obj, path, **kwargs)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Save any supported object to a file. The format is determined by the +file extension. Built-in features beyond simple save: + +- **Auto directory creation** -- no ``os.makedirs()`` needed +- **Path resolution** -- relative paths resolve to ``_out/`` +- **Symlinks** -- ``symlink_from_cwd=True`` for short access paths +- **Save logging** -- prints file path and size on success +- **Clew hash tracking** -- file hashes recorded automatically for verification +- **Figure CSV export** -- saves plot data alongside image files + +.. code-block:: python + + # Format detected from extension + stx.io.save(fig, "figure.pdf") # Also exports CSV + YAML recipe + stx.io.save(df, "output.parquet") # DataFrame + stx.io.save({"lr": 0.001}, "config.yaml") # Dict + stx.io.save(arr, "weights.npy") # NumPy array + + # Symlink for convenient access + stx.io.save(df, "results/data.csv", symlink_from_cwd=True) + +``load(path)`` +^^^^^^^^^^^^^^ + +Load data from a file. Returns the appropriate Python object +(DataFrame, ndarray, dict, etc.). + +.. code-block:: python + + df = stx.io.load("results.csv") # -> pd.DataFrame + arr = stx.io.load("weights.npy") # -> np.ndarray + cfg = stx.io.load("config.yaml") # -> dict + data = stx.io.load("experiment.h5") # -> dict of arrays + +``load_configs(pattern="./config/*.yaml")`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Load and merge multiple YAML configuration files into a single +``DotDict``. Used internally by ``@stx.session`` to build the +``CONFIG`` object. + +.. code-block:: python + + CONF = stx.io.load_configs("./config/*.yaml") + print(CONF.MODEL.hidden_size) + +``list_formats()`` +^^^^^^^^^^^^^^^^^^ + +List all registered format handlers. + +.. code-block:: python + + fmts = stx.io.list_formats() + # ['.csv', '.h5', '.hdf5', '.json', '.mat', '.npy', ...] + +HDF5 and Zarr Exploration +------------------------- + +For hierarchical formats, use the explorer interface: + +.. code-block:: python + + # HDF5 + stx.io.explore_h5("data.h5") # Print tree structure + stx.io.has_h5_key("data.h5", "/group/dataset") + + # Zarr + stx.io.explore_zarr("data.zarr") + stx.io.has_zarr_key("data.zarr", "/group/array") + +Format Registry +--------------- + +Register custom loaders and savers for new file extensions: + +.. code-block:: python + + @stx.io.register_loader(".custom") + def load_custom(path, **kwargs): + with open(path) as f: + return parse_custom(f.read()) + + @stx.io.register_saver(".custom") + def save_custom(obj, path, **kwargs): + with open(path, "w") as f: + f.write(serialize_custom(obj)) + +Caching +------- + +Built-in load caching for repeated reads of the same file: + +.. code-block:: python + + stx.io.configure_cache(maxsize=128) + data = stx.io.load("big_file.h5") # First call: reads from disk + data = stx.io.load("big_file.h5") # Second call: from cache + + stx.io.get_cache_info() # Cache hit/miss statistics + stx.io.clear_load_cache() # Flush the cache + +API Reference +------------- + +.. automodule:: scitex.io + :members: + :no-undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/linalg.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/linalg.rst.txt new file mode 100644 index 000000000..2f7ce9a85 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/linalg.rst.txt @@ -0,0 +1,7 @@ +linalg Module (``stx.linalg``) +============================== + +.. automodule:: scitex.linalg + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/linter.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/linter.rst.txt new file mode 100644 index 000000000..bd9072ba0 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/linter.rst.txt @@ -0,0 +1,191 @@ +Linter Module (``stx.linter``) +=============================== + +AST-based Python linter enforcing SciTeX conventions for reproducible +scientific code. Uses a plugin architecture where each downstream +package contributes its own rules. + +.. note:: + + ``stx.linter`` wraps the standalone + `scitex-linter `_ package. + Install with: ``pip install scitex-linter``. + +Quick Start +----------- + +.. code-block:: bash + + # Check a file + scitex linter check my_script.py + + # Check with specific severity + scitex linter check my_script.py --severity warning + + # List all rules + scitex linter list-rules + + # Filter by category + scitex linter list-rules --category session + +.. code-block:: python + + from scitex_linter import check_file, list_rules + + # Check a file + results = check_file("my_script.py") + + # List available rules + rules = list_rules() + +Rule Categories +--------------- + +47+ rules across 7 categories, contributed by multiple packages: + +.. list-table:: + :header-rows: 1 + :widths: 10 20 15 55 + + * - Prefix + - Category + - Source + - Examples + * - ``STX-S`` + - **Session** + - scitex-linter + - Missing ``@stx.session``, missing ``if __name__`` guard, missing return, + ``load_configs()`` result not UPPER_CASE, magic numbers in module scope + * - ``STX-I`` + - **Import** + - scitex-linter + - Using ``import mngs`` instead of ``import scitex``, bare ``import numpy``, + wildcard imports + * - ``STX-IO`` + - **I/O** + - scitex-io + - Using ``open()`` instead of ``stx.io``, hardcoded paths, + using ``pd.read_csv`` directly + * - ``STX-P`` + - **Plotting** + - figrecipe + - Using ``plt.show()`` instead of ``stx.io.save``, missing ``set_xyt``, + using raw matplotlib calls + * - ``STX-ST`` + - **Statistics** + - scitex-stats + - Using ``scipy.stats`` directly instead of ``stx.stats``, + missing multiple comparison correction + * - ``STX-PA`` + - **Path** + - scitex-linter + - Hardcoded absolute paths, missing ``stx.gen.mk_spath`` + * - ``STX-FM`` + - **Format** + - figrecipe + - Non-snake_case names, magic numbers, missing docstrings + +Severity Levels +--------------- + +- **error** -- Must fix (breaks reproducibility or correctness) +- **warning** -- Should fix (best practice violations) +- **info** -- Suggestions for improvement + +Plugin Architecture +------------------- + +Each downstream package defines its own linter rules via the +``scitex_linter.plugins`` entry point. The linter discovers and +aggregates rules automatically at runtime. + +.. code-block:: toml + + # In a downstream package's pyproject.toml + [project.entry-points."scitex_linter.plugins"] + my_package = "my_package._linter_plugin:get_plugin" + +The plugin function returns a dictionary of rules, call patterns, +axes hints, and AST checker classes: + +.. code-block:: python + + def get_plugin(): + from ._rules import MyRule001, MyRule002 + return { + "rules": [MyRule001, MyRule002], + "call_rules": {"my_func": "Use stx.my_func() instead"}, + "axes_hints": {}, + "checkers": [], + } + +Interfaces +---------- + +The linter is available through four interfaces: + +**CLI (via scitex):** + +.. code-block:: bash + + scitex linter check script.py + scitex linter list-rules --category io --severity warning + +**Standalone CLI:** + +.. code-block:: bash + + scitex-linter check script.py + +**Python API:** + +.. code-block:: python + + from scitex_linter import check_file, check_source, list_rules + + results = check_file("script.py") + results = check_source("import numpy as np\n...") + rules = list_rules(category="io", severity="warning") + +**Flake8 Plugin:** + +.. code-block:: bash + + flake8 --select=STX script.py + +**MCP Tools (for AI agents):** + +.. code-block:: python + + linter_check(path="script.py", severity="warning") + linter_list_rules(category="session") + linter_check_source(source="...", filepath="") + +Configuration +------------- + +Configure via ``pyproject.toml``: + +.. code-block:: toml + + [tool.scitex-linter] + severity = "warning" + ignore = ["STX-S002", "STX-FM001"] + +Example Output +-------------- + +.. code-block:: text + + my_script.py:3:1: STX-I001 [warning] Use 'import scitex as stx' instead of 'import mngs' + my_script.py:15:5: STX-IO003 [error] Use 'stx.io.load()' instead of 'pd.read_csv()' + my_script.py:22:1: STX-S001 [warning] Missing '@stx.session' decorator on main() + + 3 issues found (1 error, 2 warnings) + +API Reference +------------- + +.. automodule:: scitex.linter + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/logging.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/logging.rst.txt new file mode 100644 index 000000000..f8a5512db --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/logging.rst.txt @@ -0,0 +1,108 @@ +Logging Module (``stx.logging``) +================================= + +Unified logging with file/console output, custom warnings, exception +hierarchy, and stream redirection. + +Quick Reference +--------------- + +.. code-block:: python + + import scitex as stx + from scitex import logging + + # Get a logger + logger = logging.getLogger(__name__) + logger.info("Processing data") + logger.success("Analysis complete") # Custom level (31) + logger.fail("Model diverged") # Custom level (35) + + # Configure globally + logging.configure(level="DEBUG", log_file="./run.log") + logging.set_level("WARNING") + + # Temporary file logging + with logging.log_to_file("analysis.log"): + logger.info("This goes to both console and file") + + # Warnings + logging.warn("Large dataset", category=logging.PerformanceWarning) + logging.warn_deprecated("old_func", "new_func", version="3.0") + logging.filterwarnings("ignore", category=logging.UnitWarning) + +Log Levels +---------- + +.. list-table:: + :header-rows: 1 + :widths: 20 15 65 + + * - Level + - Value + - Description + * - ``DEBUG`` + - 10 + - Detailed diagnostic information + * - ``INFO`` + - 20 + - General operational messages + * - ``WARNING`` + - 30 + - Something unexpected but not fatal + * - ``SUCCESS`` + - 31 + - Custom: operation completed successfully + * - ``FAIL`` + - 35 + - Custom: operation failed (non-fatal) + * - ``ERROR`` + - 40 + - Serious problem + * - ``CRITICAL`` + - 50 + - Program may not continue + +Warning Categories +------------------ + +- ``SciTeXWarning`` -- Base warning class +- ``UnitWarning`` -- SI unit convention issues +- ``StyleWarning`` -- Formatting issues +- ``SciTeXDeprecationWarning`` -- Deprecated features +- ``PerformanceWarning`` -- Performance issues +- ``DataLossWarning`` -- Potential data loss + +Exception Hierarchy +------------------- + +All inherit from ``SciTeXError``: + +- **I/O**: ``IOError``, ``FileFormatError``, ``SaveError``, ``LoadError`` +- **Config**: ``ConfigurationError``, ``ConfigFileNotFoundError``, ``ConfigKeyError`` +- **Path**: ``PathError``, ``InvalidPathError``, ``PathNotFoundError`` +- **Data**: ``DataError``, ``ShapeError``, ``DTypeError`` +- **Plotting**: ``PlottingError``, ``FigureNotFoundError``, ``AxisError`` +- **Stats**: ``StatsError``, ``TestError`` +- **Scholar**: ``ScholarError``, ``SearchError``, ``PDFDownloadError``, ``DOIResolutionError`` +- **NN**: ``NNError``, ``ModelError`` +- **Template**: ``TemplateError``, ``TemplateViolationError`` + +Stream Redirection +------------------ + +.. code-block:: python + + from scitex.logging import tee + import sys + + # Redirect stdout/stderr to log files + sys.stdout, sys.stderr = tee(sys, sdir="./output") + +The ``@stx.session`` decorator handles this automatically. + +API Reference +------------- + +.. automodule:: scitex.logging + :members: diff --git a/src/scitex/_sphinx_html/_sources/source/api/media.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/media.rst.txt new file mode 100644 index 000000000..34e386c28 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/media.rst.txt @@ -0,0 +1,7 @@ +media Module (``stx.media``) +============================ + +.. automodule:: scitex.media + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/module.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/module.rst.txt new file mode 100644 index 000000000..6ba06a686 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/module.rst.txt @@ -0,0 +1,7 @@ +module Module (``stx.module``) +============================== + +.. automodule:: scitex.module + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/msword.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/msword.rst.txt new file mode 100644 index 000000000..a48391fae --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/msword.rst.txt @@ -0,0 +1,7 @@ +msword Module (``stx.msword``) +============================== + +.. automodule:: scitex.msword + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/nn.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/nn.rst.txt new file mode 100644 index 000000000..a4f886c7d --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/nn.rst.txt @@ -0,0 +1,80 @@ +NN Module (``stx.nn``) +====================== + +PyTorch neural network layers for signal processing and neuroscience +applications. + +Quick Reference +--------------- + +.. code-block:: python + + import scitex as stx + import torch + + # Bandpass filtering as a differentiable layer + bpf = stx.nn.BandPassFilter( + bands=[[4, 8], [8, 13], [13, 30]], # theta, alpha, beta + fs=256, seq_len=1024 + ) + filtered = bpf(signal) # (batch, channels, 3, 1024) + + # Power spectral density + psd = stx.nn.PSD(sample_rate=256) + power, freqs = psd(signal) + + # Phase-amplitude coupling + pac = stx.nn.PAC(seq_len=1024, fs=256) + coupling = pac(signal) + +Signal Processing Layers +------------------------- + +**Filtering** (all differentiable): + +- ``BandPassFilter(bands, fs, seq_len)`` -- Multi-band frequency filtering +- ``BandStopFilter(bands, fs, seq_len)`` -- Reject frequency bands +- ``LowPassFilter(cutoffs_hz, fs, seq_len)`` -- Anti-aliasing / smoothing +- ``HighPassFilter(cutoffs_hz, fs, seq_len)`` -- High-frequency emphasis +- ``GaussianFilter(sigma)`` -- Gaussian kernel smoothing +- ``DifferentiableBandPassFilter(...)`` -- Learnable bandpass parameters + +**Spectral Analysis**: + +- ``Spectrogram(sampling_rate, n_fft)`` -- STFT-based magnitude spectrogram +- ``PSD(sample_rate, prob)`` -- FFT-based power spectral density +- ``Wavelet(samp_rate, freq_scale)`` -- Continuous wavelet transform + +**Phase & Coupling**: + +- ``Hilbert(seq_len)`` -- Analytic signal (phase + amplitude) +- ``ModulationIndex(n_bins)`` -- Phase-amplitude coupling metric +- ``PAC(seq_len, fs, ...)`` -- Complete PAC analysis pipeline + +Channel Manipulation +-------------------- + +- ``SwapChannels()`` -- Random channel permutation (training augmentation) +- ``DropoutChannels(dropout)`` -- Drop entire channels +- ``ChannelGainChanger(n_chs)`` -- Learnable per-channel scaling +- ``FreqGainChanger(n_bands, fs)`` -- Learnable per-band scaling + +Attention & Shape +----------------- + +- ``SpatialAttention(n_chs_in)`` -- Adaptive channel weighting +- ``TransposeLayer(axis1, axis2)`` -- Dimension permutation +- ``AxiswiseDropout(dropout_prob, dim)`` -- Drop entire axis + +Architectures +------------- + +- ``ResNet1D(n_chs, n_out, n_blks)`` -- 1D residual network +- ``BNet`` / ``BNet_Res`` -- Multi-head EEG classifier +- ``MNet1000`` -- 2D CNN feature extractor + +API Reference +------------- + +.. automodule:: scitex.nn + :members: diff --git a/src/scitex/_sphinx_html/_sources/source/api/notebook.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/notebook.rst.txt new file mode 100644 index 000000000..b66a0a1c0 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/notebook.rst.txt @@ -0,0 +1,7 @@ +notebook Module (``stx.notebook``) +================================== + +.. automodule:: scitex.notebook + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/notification.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/notification.rst.txt new file mode 100644 index 000000000..33d9b0658 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/notification.rst.txt @@ -0,0 +1,134 @@ +Notification Module (``stx.notification``) +=============================== + +Multi-backend notification system for alerting researchers when +long-running tasks complete, errors occur, or results are ready. + +.. note:: + + ``stx.notification`` delegates to the standalone + `scitex-notification `_ + package. Install with: ``pip install scitex-notification``. + +Supported Backends +------------------ + +.. list-table:: + :header-rows: 1 + :widths: 20 80 + + * - Backend + - Description + * - ``desktop`` + - Native desktop notifications (Linux ``notify-send``, macOS, Windows) + * - ``email`` + - Email via SMTP + * - ``slack`` + - Slack messages via webhook or Bot API + * - ``webhook`` + - Generic HTTP POST to any URL + +Quick Start +----------- + +.. code-block:: python + + import scitex as stx + + # Send a notification (uses default backend) + stx.notification.send("Training complete! Accuracy: 95.2%") + + # Send to a specific backend + stx.notification.send("Job finished", backend="slack") + + # List available backends + stx.notification.list_backends() + +Key Functions +------------- + +``send(message, backend=None, **kwargs)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Send a notification through one or more backends. + +.. code-block:: python + + # Simple message + stx.notification.send("Experiment finished successfully.") + + # With title + stx.notification.send("95.2% accuracy achieved", title="Training Complete") + + # To a specific backend + stx.notification.send("Results ready", backend="email") + +``config(**kwargs)`` +^^^^^^^^^^^^^^^^^^^^ + +Configure notification backends. Settings persist across sessions. + +.. code-block:: python + + # Configure Slack + stx.notification.config( + slack_webhook="https://hooks.slack.com/services/...", + ) + + # Configure email + stx.notification.config( + email_to="researcher@university.edu", + email_from="scitex@lab.org", + smtp_host="smtp.university.edu", + ) + +``list_backends()`` +^^^^^^^^^^^^^^^^^^^ + +List available and configured backends. + +.. code-block:: python + + backends = stx.notification.list_backends() + # [{'name': 'desktop', 'available': True, 'configured': True}, + # {'name': 'slack', 'available': True, 'configured': False}, ...] + +Integration with ``@stx.session`` +---------------------------------- + +Enable automatic notifications when a session completes: + +.. code-block:: python + + import scitex as stx + + @stx.session(notify=True) + def main(CONFIG=stx.INJECTED): + """Long-running experiment.""" + result = train_model() + return 0 # Sends "Session FINISHED_SUCCESS" notification + + if __name__ == "__main__": + main() + +CLI Access +---------- + +.. code-block:: bash + + # Send a notification + scitex notify send "Job complete" + + # Check backend status + scitex notify backends + + # Configure a backend + scitex notify config --slack-webhook "https://..." + +API Reference +------------- + +.. automodule:: scitex.notification + :members: + :no-undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/os.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/os.rst.txt new file mode 100644 index 000000000..be2fda313 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/os.rst.txt @@ -0,0 +1,7 @@ +os Module (``stx.os``) +====================== + +.. automodule:: scitex.os + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/parallel.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/parallel.rst.txt new file mode 100644 index 000000000..620d8333d --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/parallel.rst.txt @@ -0,0 +1,7 @@ +parallel Module (``stx.parallel``) +================================== + +.. automodule:: scitex.parallel + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/path.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/path.rst.txt new file mode 100644 index 000000000..672ff6967 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/path.rst.txt @@ -0,0 +1,7 @@ +path Module (``stx.path``) +========================== + +.. automodule:: scitex.path + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/pd.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/pd.rst.txt new file mode 100644 index 000000000..9a34275d0 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/pd.rst.txt @@ -0,0 +1,46 @@ +PD Module (``stx.pd``) +====================== + +Pandas DataFrame helper functions for common transformations. + +Quick Reference +--------------- + +.. code-block:: python + + import scitex as stx + import pandas as pd + + df = pd.DataFrame({ + "subject": ["A", "A", "B", "B"], + "condition": ["ctrl", "exp", "ctrl", "exp"], + "score": [10, 15, 12, 18], + }) + + # Find individual (unique) values + subjects = stx.pd.find_indi(df, "subject") + + # Convert to long format + melted = stx.pd.melt_cols(df, id_vars=["subject"]) + + # Merge columns + merged = stx.pd.merge_cols(df, cols=["subject", "condition"], sep="_") + + # Force to DataFrame + stx.pd.force_df({"a": [1, 2], "b": [3, 4]}) + +Available Functions +------------------- + +- ``find_indi(df, col)`` -- Find unique individual identifiers +- ``find_pval(df)`` -- Find p-value columns in a DataFrame +- ``force_df(data)`` -- Convert any data to a DataFrame +- ``melt_cols(df, id_vars)`` -- Melt columns to long format +- ``merge_cols(df, cols, sep)`` -- Merge multiple columns into one +- ``to_xyz(df, x, y, z)`` -- Reshape to x, y, z pivot format + +API Reference +------------- + +.. automodule:: scitex.pd + :members: diff --git a/src/scitex/_sphinx_html/_sources/source/api/plt.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/plt.rst.txt new file mode 100644 index 000000000..82b3d0cf0 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/plt.rst.txt @@ -0,0 +1,243 @@ +scitex.plt +========== + +Figure and Axis Creation +------------------------ + +.. autofunction:: scitex.plt.subplots + +.. autofunction:: scitex.plt.figure + +Gallery +------- + +.. automodule:: scitex.plt.gallery + :members: + :undoc-members: + +Axis Methods +------------ + +The following methods are available on ``ax`` objects returned by ``stx.plt.subplots()``. + +Line Plots +~~~~~~~~~~ + +.. code-block:: python + + fig, ax = stx.plt.subplots() + +.. py:method:: ax.stx_line(values_1d, xx=None, track=True, id=None, **kwargs) + + Plot a simple line from 1D array. + + :param values_1d: Y values + :param xx: Optional X values + :param track: Track data for CSV export + :param id: Identifier for tracking + +.. py:method:: ax.stx_shaded_line(xs, ys_lower, ys_middle, ys_upper, color=None, label=None, track=True, id=None, **kwargs) + + Plot a line with shaded area between bounds. + + :param xs: X values + :param ys_lower: Lower bound Y values + :param ys_middle: Middle line Y values + :param ys_upper: Upper bound Y values + +Statistical Plots +~~~~~~~~~~~~~~~~~ + +.. py:method:: ax.stx_mean_std(values_2d, xx=None, sd=1, track=True, id=None, **kwargs) + + Plot mean line with standard deviation shading. + + :param values_2d: 2D array (samples x timepoints) + :param xx: Optional X values + :param sd: Number of standard deviations for shading + +.. py:method:: ax.stx_mean_ci(values_2d, xx=None, perc=95, track=True, id=None, **kwargs) + + Plot mean line with confidence interval shading. + + :param values_2d: 2D array (samples x timepoints) + :param xx: Optional X values + :param perc: Confidence interval percentage (default: 95) + +.. py:method:: ax.stx_median_iqr(values_2d, xx=None, track=True, id=None, **kwargs) + + Plot median line with interquartile range shading. + + :param values_2d: 2D array (samples x timepoints) + :param xx: Optional X values + +.. py:method:: ax.stx_errorbar(x, y, yerr=None, xerr=None, track=True, id=None, **kwargs) + + Error bar plot with scitex styling. + + :param x: X coordinates + :param y: Y coordinates + :param yerr: Y error values + :param xerr: X error values + +Distribution Plots +~~~~~~~~~~~~~~~~~~ + +.. py:method:: ax.stx_kde(values_1d, cumulative=False, fill=False, track=True, id=None, **kwargs) + + Plot kernel density estimate. + + :param values_1d: 1D array of values + :param cumulative: Plot cumulative distribution + :param fill: Fill under curve + +.. py:method:: ax.stx_ecdf(values_1d, track=True, id=None, **kwargs) + + Plot empirical cumulative distribution function. + + :param values_1d: 1D array of values + +.. py:method:: ax.hist(x, bins=10, range=None, align_bins=True, track=True, id=None, **kwargs) + + Plot histogram with bin alignment support. + + :param x: Input data + :param bins: Number of bins or bin edges + :param align_bins: Align bins with other histograms on same axis + +Categorical Plots +~~~~~~~~~~~~~~~~~ + +.. py:method:: ax.stx_box(values_list, colors=None, track=True, id=None, **kwargs) + + Boxplot with scitex styling. + + :param values_list: List of arrays for each box + :param colors: Optional colors for boxes + +.. py:method:: ax.stx_violin(values_list, x=None, y=None, hue=None, labels=None, colors=None, half=False, track=True, id=None, **kwargs) + + Violin plot with scitex styling. + + :param values_list: List of arrays or DataFrame + :param half: Show half violins + +Scatter Plots +~~~~~~~~~~~~~ + +.. py:method:: ax.stx_scatter(x, y, track=True, id=None, **kwargs) + + Scatter plot with scitex styling. + + :param x: X coordinates + :param y: Y coordinates + +.. py:method:: ax.stx_scatter_hist(x, y, hist_bins=20, scatter_alpha=0.6, track=True, id=None, **kwargs) + + Scatter plot with marginal histograms. + + :param x: X coordinates + :param y: Y coordinates + :param hist_bins: Number of histogram bins + +Bar Plots +~~~~~~~~~ + +.. py:method:: ax.stx_bar(x, height, track=True, id=None, **kwargs) + + Vertical bar plot with scitex styling. + + :param x: X coordinates + :param height: Bar heights + +.. py:method:: ax.stx_barh(y, width, track=True, id=None, **kwargs) + + Horizontal bar plot with scitex styling. + + :param y: Y coordinates + :param width: Bar widths + +Heatmaps +~~~~~~~~ + +.. py:method:: ax.stx_heatmap(values_2d, x_labels=None, y_labels=None, cmap="viridis", cbar_label="", show_annot=True, track=True, id=None, **kwargs) + + Plot annotated heatmap. + + :param values_2d: 2D array + :param x_labels: Column labels + :param y_labels: Row labels + :param show_annot: Show value annotations + +.. py:method:: ax.stx_conf_mat(conf_mat_2d, x_labels=None, y_labels=None, title="Confusion Matrix", track=True, id=None, **kwargs) + + Plot confusion matrix. + + :param conf_mat_2d: 2D confusion matrix array + :param x_labels: Predicted class labels + :param y_labels: True class labels + +.. py:method:: ax.stx_image(arr_2d, track=True, id=None, **kwargs) + + Display 2D array as image. + + :param arr_2d: 2D array + +Special Plots +~~~~~~~~~~~~~ + +.. py:method:: ax.stx_raster(spike_times_list, time=None, labels=None, colors=None, track=True, id=None, **kwargs) + + Plot spike raster. + + :param spike_times_list: List of spike time arrays per neuron + :param time: Optional time array + +.. py:method:: ax.stx_fillv(starts_1d, ends_1d, color="red", alpha=0.2, track=True, id=None, **kwargs) + + Fill vertical regions. + + :param starts_1d: Start positions + :param ends_1d: End positions + +.. py:method:: ax.stx_rectangle(xx, yy, width, height, track=True, id=None, **kwargs) + + Draw rectangle. + + :param xx: X position + :param yy: Y position + :param width: Rectangle width + :param height: Rectangle height + +.. py:method:: ax.stx_joyplot(arrays, track=True, id=None, **kwargs) + + Plot joyplot (ridgeline plot). + + :param arrays: List of arrays + +Seaborn Wrappers +~~~~~~~~~~~~~~~~ + +.. py:method:: ax.sns_barplot(data=None, x=None, y=None, track=True, id=None, **kwargs) +.. py:method:: ax.sns_boxplot(data=None, x=None, y=None, strip=False, track=True, id=None, **kwargs) +.. py:method:: ax.sns_violinplot(data=None, x=None, y=None, track=True, id=None, **kwargs) +.. py:method:: ax.sns_stripplot(data=None, x=None, y=None, track=True, id=None, **kwargs) +.. py:method:: ax.sns_swarmplot(data=None, x=None, y=None, track=True, id=None, **kwargs) +.. py:method:: ax.sns_heatmap(*args, xyz=False, track=True, id=None, **kwargs) +.. py:method:: ax.sns_histplot(data=None, x=None, y=None, bins=10, track=True, id=None, **kwargs) +.. py:method:: ax.sns_kdeplot(data=None, x=None, y=None, track=True, id=None, **kwargs) +.. py:method:: ax.sns_scatterplot(data=None, x=None, y=None, track=True, id=None, **kwargs) +.. py:method:: ax.sns_lineplot(data=None, x=None, y=None, track=True, id=None, **kwargs) +.. py:method:: ax.sns_jointplot(*args, track=True, id=None, **kwargs) +.. py:method:: ax.sns_pairplot(*args, track=True, id=None, **kwargs) + +Axis Utilities +~~~~~~~~~~~~~~ + +.. py:method:: ax.set_xyt(x=None, y=None, t=None) + + Set xlabel, ylabel, and title in one call. + +.. py:method:: ax.hide_spines(*spines) + + Hide specified spines (top, right, bottom, left). diff --git a/src/scitex/_sphinx_html/_sources/source/api/project.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/project.rst.txt new file mode 100644 index 000000000..b0440a59f --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/project.rst.txt @@ -0,0 +1,7 @@ +project Module (``stx.project``) +================================ + +.. automodule:: scitex.project + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/repro.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/repro.rst.txt new file mode 100644 index 000000000..0e8dc4fea --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/repro.rst.txt @@ -0,0 +1,71 @@ +Repro Module (``stx.repro``) +============================ + +Reproducibility utilities: random state management, ID generation, +timestamps, and array hashing. + +Quick Reference +--------------- + +.. code-block:: python + + import scitex as stx + + # Fix all random seeds (numpy, torch, random, ...) + rng = stx.repro.get() # Global manager (seed=42) + rng = stx.repro.reset(seed=123) # Reset with new seed + + # Named generators for deterministic results + data_gen = rng.get_np_generator("data") + data = data_gen.random(100) # Same seed+name = same result + + # Unique identifiers + stx.repro.gen_id() + # → "2026Y-02M-13D-14h30m15s_a3Bc9xY2" + + stx.repro.gen_timestamp() + # → "2026-0213-1430" + + # Verify reproducibility + rng.verify(data, "train_data") # First: caches hash + rng.verify(data, "train_data") # Later: verifies match + +RandomStateManager +------------------ + +Central class for managing random states across libraries. + +.. code-block:: python + + rng = stx.repro.RandomStateManager(seed=42) + + # Named generators (same name + seed = deterministic) + np_gen = rng.get_np_generator("experiment") + torch_gen = rng.get_torch_generator("model") + + # Checkpoint and restore + rng.checkpoint("before_training") + rng.restore("before_training.pkl") + + # Temporary seed change + with rng.temporary_seed(999): + noise = rng.get_np_generator("noise").random(10) + +Automatically fixes seeds for: ``random``, ``numpy``, ``torch`` (+ CUDA), +``tensorflow``, ``jax``. + +Available Functions +------------------- + +- ``get(verbose)`` -- Get or create global RandomStateManager singleton +- ``reset(seed, verbose)`` -- Reset global instance with new seed +- ``fix_seeds(seed, ...)`` -- Legacy function (use RandomStateManager instead) +- ``gen_id(time_format, N)`` -- Generate unique timestamp + random ID +- ``gen_timestamp()`` -- Generate timestamp string for file naming +- ``hash_array(array_data)`` -- SHA256 hash of numpy array (16 chars) + +API Reference +------------- + +.. automodule:: scitex.repro + :members: diff --git a/src/scitex/_sphinx_html/_sources/source/api/resource.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/resource.rst.txt new file mode 100644 index 000000000..d4fbcc001 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/resource.rst.txt @@ -0,0 +1,7 @@ +resource Module (``stx.resource``) +================================== + +.. automodule:: scitex.resource + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/rng.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/rng.rst.txt new file mode 100644 index 000000000..a6ab74def --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/rng.rst.txt @@ -0,0 +1,7 @@ +rng Module (``stx.rng``) +======================== + +.. automodule:: scitex.rng + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/schema.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/schema.rst.txt new file mode 100644 index 000000000..919582dbf --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/schema.rst.txt @@ -0,0 +1,7 @@ +schema Module (``stx.schema``) +============================== + +.. automodule:: scitex.schema + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/scholar.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/scholar.rst.txt new file mode 100644 index 000000000..2be71119e --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/scholar.rst.txt @@ -0,0 +1,133 @@ +Scholar Module (``stx.scholar``) +================================= + +Literature management: search papers, download PDFs, enrich BibTeX, +and organize a local library across multiple projects. + +Quick Reference +--------------- + +.. code-block:: python + + from scitex.scholar import Scholar + + scholar = Scholar(project="my_research") + + # Load and enrich BibTeX + papers = scholar.load_bibtex("references.bib") + enriched = scholar.enrich_papers(papers) + # Adds: DOIs, abstracts, citation counts, impact factors + + # Save to library and export + scholar.save_papers_to_library(enriched) + scholar.save_papers_as_bibtex(enriched, "enriched.bib") + + # Search your library + results = scholar.search_library("neural oscillations") + + # Download PDFs + scholar.download_pdfs(dois, output_dir) + +CLI Usage +--------- + +.. code-block:: bash + + # Full pipeline from BibTeX + scitex scholar bibtex refs.bib --project myresearch --num-workers 8 + + # Search papers + scitex scholar search "deep learning EEG" + + # Download PDFs + scitex scholar download --doi 10.1038/nature12373 + + # Institutional authentication + scitex scholar auth --method openathens + scitex scholar auth --method shibboleth --institution "MIT" + +Data Sources +------------ + +Searches and enriches from: + +- **CrossRef** (167M+ papers) -- DOI resolution, citation counts +- **Semantic Scholar** -- Abstracts, references, influence scores +- **PubMed** -- Biomedical literature +- **arXiv** -- Preprints +- **OpenAlex** (284M+ works) -- Open metadata + +Key Classes +----------- + +- ``Scholar`` -- Main entry point (search, enrich, download, organize) +- ``Paper`` -- Type-safe metadata container (Pydantic model) +- ``Papers`` -- Collection with filtering, sorting, and export +- ``ScholarConfig`` -- YAML-based configuration +- ``ScholarLibrary`` -- Local library storage and caching + +Paper Metadata +-------------- + +Each ``Paper`` contains structured metadata sections: + +.. code-block:: python + + paper.metadata.basic # title, authors, year, abstract, keywords + paper.metadata.id # DOI, arXiv, PMID, Semantic Scholar ID + paper.metadata.publication # journal, impact factor, volume, issue + paper.metadata.citation_count # total + yearly breakdown (2015--2024) + paper.metadata.url # DOI URL, publisher, arXiv, PDFs + paper.metadata.access # open access status, license + +Filtering and Sorting +--------------------- + +.. code-block:: python + + # Criteria-based filtering + recent = papers.filter(year_min=2020, has_doi=True) + elite = papers.filter(min_impact_factor=10, min_citations=500) + + # Lambda filtering + custom = papers.filter(lambda p: "EEG" in (p.metadata.basic.title or "")) + + # Sorting + papers.sort_by("year", reverse=True) + papers.sort_by("citation_count", reverse=True) + + # Chaining + top_recent = papers.filter(year_min=2020).sort_by("citation_count", reverse=True) + +Project Organization +-------------------- + +.. code-block:: python + + scholar = Scholar(project="review_paper") + scholar.list_projects() + papers = scholar.load_project() + + # Export to multiple formats + scholar.save_papers_as_bibtex(papers, "output.bib") + papers.to_dataframe() # pandas DataFrame + +Storage Architecture +-------------------- + +.. code-block:: text + + ~/.scitex/scholar/library/ + +-- MASTER/ # Centralized master storage + | +-- 8DIGIT01/ # Hash-based unique ID from DOI + | | +-- metadata.json + | | +-- paper.pdf + +-- project_name/ # Project-specific symlinks + +-- Author-Year-Journal -> ../MASTER/8DIGIT01 + +API Reference +------------- + +.. automodule:: scitex.scholar + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/security.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/security.rst.txt new file mode 100644 index 000000000..232a5da7f --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/security.rst.txt @@ -0,0 +1,7 @@ +security Module (``stx.security``) +================================== + +.. automodule:: scitex.security + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/session.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/session.rst.txt new file mode 100644 index 000000000..8f16f4670 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/session.rst.txt @@ -0,0 +1,213 @@ +Session Decorator (``@stx.session``) +===================================== + +The ``@stx.session`` decorator is the primary entry point for SciTeX +scripts. It wraps a function to provide automatic CLI generation, +output directory management, configuration injection, and provenance tracking. + +Basic Usage +----------- + +.. code-block:: python + + import scitex as stx + + @stx.session + def main( + data_path="data.csv", # CLI: --data-path data.csv + threshold=0.5, # CLI: --threshold 0.7 + CONFIG=stx.INJECTED, # Auto-injected session config + plt=stx.INJECTED, # Pre-configured matplotlib + logger=stx.INJECTED, # Session logger + ): + """Analyze experimental data.""" + data = stx.io.load(data_path) + result = process(data, threshold) + stx.io.save(result, "output.csv") + return 0 # exit code (0 = success) + + if __name__ == "__main__": + main() # No arguments triggers CLI mode + +Run from the command line: + +.. code-block:: bash + + python my_script.py --data-path experiment.csv --threshold 0.3 + python my_script.py --help # Shows all parameters with defaults + +How It Works +------------ + +.. image:: ../_static/session_lifecycle.png + :width: 60% + :align: center + :alt: Session lifecycle: main() → Parse CLI → Create dir → Load config → Execute → SUCCESS or ERROR + +When ``main()`` is called **without arguments**, the decorator: + +1. **Parses CLI arguments** from the function signature (type hints and defaults become ``argparse`` options) +2. **Creates a session directory** under ``script_out/RUNNING/{session_id}/`` +3. **Loads configuration** from ``./config/*.yaml`` files into ``CONFIG`` +4. **Injects globals** (``CONFIG``, ``plt``, ``COLORS``, ``logger``, ``rngg``) into the function +5. **Redirects stdout/stderr** to log files +6. **Executes the function** with parsed arguments +7. **Moves output** from ``RUNNING/`` to ``FINISHED_SUCCESS/`` (or ``FINISHED_ERROR/``) + +When called **with arguments** (e.g., ``main(data_path="x.csv")``), the decorator +is bypassed and the function runs directly -- useful for testing and notebooks. + +Output Directory Structure +-------------------------- + +Each run produces a self-contained output directory: + +.. code-block:: text + + my_script_out/ + +-- FINISHED_SUCCESS/ + | +-- 2026Y-02M-13D-14h30m15s_Z5MR/ + | +-- CONFIGS/ + | | +-- CONFIG.yaml # Frozen configuration + | | +-- CONFIG.pkl # Pickled config + | +-- logs/ + | | +-- stdout.log # Captured stdout + | | +-- stderr.log # Captured stderr + | +-- output.csv # Your saved files + | +-- sine.png # Your saved figures + | +-- sine.csv # Auto-exported figure data + +-- FINISHED_ERROR/ + | +-- ... # Runs that returned non-zero + +-- RUNNING/ + +-- ... # Currently active sessions + +The session ID format is ``YYYY-MM-DD-HH:MM:SS_XXXX`` where ``XXXX`` is +a random 4-character suffix for uniqueness. + +Injected Parameters +------------------- + +Use ``stx.INJECTED`` as the default value to receive auto-injected objects: + +.. list-table:: + :header-rows: 1 + :widths: 20 20 60 + + * - Parameter Name + - Type + - Description + * - ``CONFIG`` + - ``DotDict`` + - Session config with ``ID``, ``SDIR_RUN``, ``FILE``, ``ARGS``, plus all ``./config/*.yaml`` values + * - ``plt`` + - module + - ``matplotlib.pyplot`` configured for the session (Agg backend, style settings) + * - ``COLORS`` + - ``DotDict`` + - Color palette for consistent plotting + * - ``logger`` + - Logger + - SciTeX logger writing to session log files + * - ``rngg`` + - ``RandomStateManager`` + - Reproducibility manager (seeds fixed by default) + +Only request the parameters you need: + +.. code-block:: python + + @stx.session + def main(n=100, CONFIG=stx.INJECTED): + """Minimal example -- only CONFIG injected.""" + print(f"Session ID: {CONFIG.ID}") + print(f"Output dir: {CONFIG.SDIR_RUN}") + return 0 + +CONFIG Object +------------- + +The injected ``CONFIG`` is a ``DotDict`` supporting both dictionary and +dot-notation access: + +.. code-block:: python + + CONFIG["MODEL"]["hidden_size"] # dict-style + CONFIG.MODEL.hidden_size # dot-style (equivalent) + +It contains: + +.. code-block:: python + + CONFIG.ID # "2026Y-02M-13D-14h30m15s_Z5MR" + CONFIG.PID # Process ID + CONFIG.FILE # Path to the script + CONFIG.SDIR_OUT # Base output directory + CONFIG.SDIR_RUN # Current session's output directory + CONFIG.START_DATETIME # When the session started + CONFIG.ARGS # Parsed CLI arguments as dict + CONFIG.EXIT_STATUS # 0 (success), 1 (error), or None + +Any YAML files in ``./config/`` are merged into CONFIG: + +.. code-block:: yaml + + # ./config/EXPERIMENT.yaml + learning_rate: 0.001 + batch_size: 32 + +.. code-block:: python + + # Accessible as: + CONFIG.EXPERIMENT.learning_rate # 0.001 + CONFIG.EXPERIMENT.batch_size # 32 + +Decorator Options +----------------- + +.. code-block:: python + + @stx.session(verbose=True, agg=False, notify=True, sdir_suffix="v2") + def main(...): + ... + +.. list-table:: + :header-rows: 1 + :widths: 20 15 15 50 + + * - Option + - Type + - Default + - Description + * - ``verbose`` + - bool + - ``False`` + - Enable verbose logging + * - ``agg`` + - bool + - ``True`` + - Use matplotlib Agg backend (set ``False`` for interactive plots) + * - ``notify`` + - bool + - ``False`` + - Send notification when session completes + * - ``sdir_suffix`` + - str + - ``None`` + - Append suffix to output directory name + +Best Practices +-------------- + +1. **One session per script** -- each ``.py`` file should have one ``@stx.session`` function +2. **Return 0 for success** -- the return value becomes the exit status +3. **Save outputs with** ``stx.io.save`` -- files go into the session directory and are provenance-tracked +4. **Put config in YAML** -- use ``./config/*.yaml`` instead of hardcoding parameters +5. **Use** ``stx.repro.fix_seeds()`` -- already called by default via ``rngg`` + +API Reference +------------- + +.. automodule:: scitex.session + :members: + :no-undoc-members: + :exclude-members: _InjectedSentinel diff --git a/src/scitex/_sphinx_html/_sources/source/api/sh.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/sh.rst.txt new file mode 100644 index 000000000..7c4694af6 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/sh.rst.txt @@ -0,0 +1,7 @@ +sh Module (``stx.sh``) +====================== + +.. automodule:: scitex.sh + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/social.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/social.rst.txt new file mode 100644 index 000000000..cc82af401 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/social.rst.txt @@ -0,0 +1,7 @@ +social Module (``stx.social``) +============================== + +.. automodule:: scitex.social + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/stats.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/stats.rst.txt new file mode 100644 index 000000000..17ca488dd --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/stats.rst.txt @@ -0,0 +1,153 @@ +Stats Module (``stx.stats``) +============================ + +23 statistical tests with effect sizes, confidence intervals, and +publication-ready formatting. + +Quick Reference +--------------- + +.. code-block:: python + + import scitex as stx + import numpy as np + + g1 = np.random.normal(0, 1, 50) + g2 = np.random.normal(0.5, 1, 50) + + # Run a test + result = stx.stats.test_ttest_ind(g1, g2) + + # Result is a flat dict with all details + print(result["statistic"]) # t-statistic + print(result["pvalue"]) # p-value + print(result["effect_size"]) # Cohen's d + print(result["stars"]) # Significance stars + + # Get as DataFrame or LaTeX + df = stx.stats.test_ttest_ind(g1, g2, return_as="dataframe") + tex = stx.stats.test_ttest_ind(g1, g2, return_as="latex") + +Available Tests +--------------- + +**Parametric** + +- ``test_ttest_1samp(data, popmean)`` -- One-sample t-test +- ``test_ttest_ind(g1, g2)`` -- Independent two-sample t-test +- ``test_ttest_rel(g1, g2)`` -- Paired t-test +- ``test_anova(*groups)`` -- One-way ANOVA +- ``test_anova_2way(data, factor1, factor2)`` -- Two-way ANOVA +- ``test_anova_rm(data, groups)`` -- Repeated measures ANOVA + +**Non-parametric** + +- ``test_mannwhitneyu(g1, g2)`` -- Mann-Whitney U test +- ``test_wilcoxon(g1, g2)`` -- Wilcoxon signed-rank test +- ``test_kruskal(*groups)`` -- Kruskal-Wallis H test +- ``test_friedman(*groups)`` -- Friedman test +- ``test_brunner_munzel(g1, g2)`` -- Brunner-Munzel test + +**Correlation** + +- ``test_pearson(x, y)`` -- Pearson correlation +- ``test_spearman(x, y)`` -- Spearman rank correlation +- ``test_kendall(x, y)`` -- Kendall tau correlation +- ``test_theilsen(x, y)`` -- Theil-Sen robust regression + +**Categorical** + +- ``test_chi2(observed)`` -- Chi-squared test +- ``test_fisher(table)`` -- Fisher's exact test +- ``test_mcnemar(table)`` -- McNemar test +- ``test_cochran_q(*groups)`` -- Cochran's Q test + +**Normality** + +- ``test_shapiro(data)`` -- Shapiro-Wilk test +- ``test_ks_1samp(data)`` -- One-sample Kolmogorov-Smirnov test +- ``test_ks_2samp(x, y)`` -- Two-sample Kolmogorov-Smirnov test +- ``test_normality(*samples)`` -- Multi-sample normality check + +Seaborn-Style Data Parameter +---------------------------- + +All two-sample and one-sample tests accept an optional ``data`` parameter +for DataFrame/CSV column resolution (like seaborn): + +.. code-block:: python + + import pandas as pd + + df = pd.read_csv("experiment.csv") + + # Two-sample: column names as x/y + result = stx.stats.test_ttest_ind(x="before", y="after", data=df) + + # One-sample: column name as x + result = stx.stats.test_shapiro(x="scores", data=df) + + # Multi-group: value + group columns + result = stx.stats.test_anova(data=df, value_col="score", group_col="treatment") + + # Also works with CSV path + result = stx.stats.test_ttest_ind(x="col1", y="col2", data="data.csv") + +Test Recommendation +------------------- + +Not sure which test to use? Let SciTeX recommend: + +.. code-block:: python + + recommendations = stx.stats.recommend_tests( + n_groups=2, + sample_sizes=[30, 35], + outcome_type="continuous", + paired=False, + ) + +Output Formats +-------------- + +Every test supports ``return_as`` parameter: + +- ``"dict"`` (default) -- Returns plain dict with all results +- ``"dataframe"`` -- Returns pandas DataFrame + +Descriptive Statistics +---------------------- + +.. code-block:: python + + stx.stats.describe(data) # mean, std, median, IQR, etc. + stx.stats.effect_sizes.cohens_d(g1, g2) # Cohen's d + stx.stats.test_normality(g1, g2) # Multi-sample normality + stx.stats.p_to_stars(0.003) # "**" + +Multiple Comparison Correction +------------------------------ + +.. code-block:: python + + corrected = stx.stats.correct_pvalues( + [0.01, 0.03, 0.05, 0.001], + method="fdr_bh", + ) + +Post-hoc Tests +-------------- + +.. code-block:: python + + stx.stats.posthoc_test( + [g1, g2, g3], + group_names=["Control", "Treatment A", "Treatment B"], + method="tukey", + ) + +API Reference +------------- + +.. automodule:: scitex.stats + :members: diff --git a/src/scitex/_sphinx_html/_sources/source/api/str.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/str.rst.txt new file mode 100644 index 000000000..b29b4f485 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/str.rst.txt @@ -0,0 +1,7 @@ +str Module (``stx.str``) +======================== + +.. automodule:: scitex.str + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/template.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/template.rst.txt new file mode 100644 index 000000000..1a8984122 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/template.rst.txt @@ -0,0 +1,144 @@ +Template Module (``stx.template``) +==================================== + +Project scaffolding and code snippet templates for quick starts. +Create fully structured research projects, Python packages, or +academic papers with a single command. + +Project Templates +----------------- + +.. code-block:: bash + + # Clone a project template + scitex template clone research my_project + scitex template clone pip_project my_package + scitex template clone paper my_paper + scitex template clone app my_app + scitex template clone singularity my_container + +.. list-table:: + :header-rows: 1 + :widths: 25 75 + + * - Template + - Description + * - ``research`` + - Full scientific workflow (scripts, data, docs, config, session scaffolding) + * - ``research_minimal`` + - Essential modules only for lightweight experiments + * - ``pip_project`` + - Distributable Python package for PyPI (``pyproject.toml``, tests, CI) + * - ``paper`` + - Academic paper with LaTeX/BibTeX structure, SciTeX writer integration + * - ``app`` + - SciTeX application with bridge-init, MountPoint, and EventBus + * - ``singularity`` + - Reproducible containerized environment (Singularity/Apptainer definition) + +What You Get +^^^^^^^^^^^^ + +A ``research`` template creates: + +.. code-block:: text + + my_project/ + +-- config/ + | +-- EXPERIMENT.yaml # Experiment parameters + +-- scripts/ + | +-- main.py # @stx.session entry point + +-- data/ # Raw data directory + +-- docs/ # Documentation + +-- tests/ # Test suite + +-- Makefile # Common tasks + +-- pyproject.toml # Package metadata + +Git Strategies +-------------- + +Control how git is initialized in the cloned template: + +.. list-table:: + :header-rows: 1 + :widths: 20 80 + + * - Strategy + - Behavior + * - ``child`` (default) + - Fresh git repo, isolated from template + * - ``parent`` + - Use parent repo if available + * - ``origin`` + - Preserve template's git history + * - ``None`` + - No git initialization + +.. code-block:: python + + from scitex.template import clone_template + + clone_template("research", "my_project", git_strategy="child") + +Code Templates +-------------- + +Ready-to-use code snippets for common SciTeX patterns. Each snippet +is a complete, runnable script. + +.. code-block:: bash + + # List available code templates + scitex template code list + + # Get a template (prints to stdout) + scitex template code session # Full @stx.session script + scitex template code session-minimal # Lightweight session + scitex template code session-plot # Figure-focused session + scitex template code session-stats # Statistics-focused session + scitex template code io # I/O operations + scitex template code plt # Plotting patterns + scitex template code stats # Statistical analysis + scitex template code scholar # Literature management + + # Save to a file + scitex template code session --output analyze.py + +Python API +---------- + +.. code-block:: python + + from scitex.template import clone_template, get_code_template + + # Clone a project template + clone_template("research", "my_experiment", git_strategy="child") + + # Get a code snippet as a string + code = get_code_template("session") + print(code) + + # Get a code snippet and write to file + code = get_code_template("session", filepath="analyze.py") + +MCP Interface +------------- + +Code templates are also available to AI agents via MCP tools: + +.. code-block:: python + + # List templates + template_list_code_templates() + + # Get a specific template + template_get_code_template(name="session-plot") + + # Clone a project + template_clone_template(template_type="research", name="my_project") + +API Reference +------------- + +.. automodule:: scitex.template + :members: diff --git a/src/scitex/_sphinx_html/_sources/source/api/tex.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/tex.rst.txt new file mode 100644 index 000000000..e0f775bd9 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/tex.rst.txt @@ -0,0 +1,7 @@ +tex Module (``stx.tex``) +======================== + +.. automodule:: scitex.tex + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/torch.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/torch.rst.txt new file mode 100644 index 000000000..1a11fe241 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/torch.rst.txt @@ -0,0 +1,7 @@ +torch Module (``stx.torch``) +============================ + +.. automodule:: scitex.torch + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/tunnel.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/tunnel.rst.txt new file mode 100644 index 000000000..e28928527 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/tunnel.rst.txt @@ -0,0 +1,7 @@ +tunnel Module (``stx.tunnel``) +============================== + +.. automodule:: scitex.tunnel + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/types.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/types.rst.txt new file mode 100644 index 000000000..f4f1e1031 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/types.rst.txt @@ -0,0 +1,7 @@ +types Module (``stx.types``) +============================ + +.. automodule:: scitex.types + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/ui.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/ui.rst.txt new file mode 100644 index 000000000..2a8a65a64 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/ui.rst.txt @@ -0,0 +1,7 @@ +ui Module (``stx.ui``) +====================== + +.. automodule:: scitex.ui + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/utils.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/utils.rst.txt new file mode 100644 index 000000000..9b893ac20 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/utils.rst.txt @@ -0,0 +1,7 @@ +utils Module (``stx.utils``) +============================ + +.. automodule:: scitex.utils + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/web.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/web.rst.txt new file mode 100644 index 000000000..5b6589d9d --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/web.rst.txt @@ -0,0 +1,7 @@ +web Module (``stx.web``) +======================== + +.. automodule:: scitex.web + :members: + :undoc-members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/api/writer.rst.txt b/src/scitex/_sphinx_html/_sources/source/api/writer.rst.txt new file mode 100644 index 000000000..c03eaeee1 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/api/writer.rst.txt @@ -0,0 +1,166 @@ +Writer Module (``stx.writer``) +============================== + +LaTeX manuscript compilation, figure/table management, bibliography +handling, and IMRAD writing guidelines. + +.. note:: + + ``stx.writer`` wraps the standalone + `scitex-writer `_ package. + Install with: ``pip install scitex-writer``. + +Quick Reference +--------------- + +.. code-block:: python + + from scitex.writer import Writer + from pathlib import Path + + writer = Writer(Path("my_paper")) + + # Compile manuscript → PDF + result = writer.compile_manuscript() + if result.success: + print(f"PDF: {result.output_pdf}") + + # Compile supplementary materials + result = writer.compile_supplementary() + + # Compile revision with change tracking + result = writer.compile_revision(track_changes=True) + +CLI Usage +--------- + +.. code-block:: bash + + # Compile + scitex writer compile manuscript ./my-paper + scitex writer compile supplementary ./my-paper + scitex writer compile revision ./my-paper --track-changes + + # Bibliography + scitex writer bib list ./my-paper + scitex writer bib add ./my-paper "@article{...}" + + # Tables and figures + scitex writer tables add ./my-paper data.csv + scitex writer figures list ./my-paper + + # Writing guidelines + scitex writer guidelines get abstract + +Project Structure +----------------- + +A writer project follows this layout: + +.. code-block:: text + + my_paper/ + +-- 00_shared/ + | +-- bibliography.bib + | +-- figures/ + | +-- tables/ + +-- 01_manuscript/ + | +-- main.tex + | +-- sections/ + +-- 02_supplementary/ + | +-- main.tex + +-- 03_revision/ + +-- main.tex + +Create a new project: + +.. code-block:: bash + + scitex template clone paper my_paper + +Key Classes +----------- + +- ``Writer`` -- Main entry point for compilation and project management +- ``CompilationResult`` -- Compilation outcome (success, output path, logs) +- ``ManuscriptTree`` -- Document tree for the main manuscript +- ``SupplementaryTree`` -- Document tree for supplementary materials +- ``RevisionTree`` -- Document tree for revision responses + +Document Management +------------------- + +**Figures:** + +.. code-block:: python + + # Add figure (copies image + creates caption file) + writer.add_figure("fig1", "plot.png", caption="Results") + + # List figures + writer.list_figures() + + # Convert between formats + writer.convert_figure("figure.pdf", "figure.png", dpi=300) + +**Tables:** + +.. code-block:: python + + # Add table from CSV + writer.add_table("tab1", csv_content, caption="Demographics") + + # CSV ↔ LaTeX conversion + writer.csv_to_latex("data.csv", "table.tex") + writer.latex_to_csv("table.tex", "data.csv") + +**Bibliography:** + +.. code-block:: python + + # Add BibTeX entry + writer.add_bibentry('@article{key, title={...}, ...}') + + # Merge multiple .bib files + writer.merge_bibfiles(output_file="bibliography.bib") + +Writing Guidelines +------------------ + +IMRAD guidelines for each manuscript section: + +.. code-block:: bash + + scitex writer guidelines list + scitex writer guidelines get introduction + +.. code-block:: python + + from scitex.writer import guidelines + + # Get guideline for a section + guide = guidelines.get("methods") + + # Build editing prompt: guideline + draft + prompt = guidelines.build("discussion", draft_text) + +Compilation Options +------------------- + +.. code-block:: python + + result = writer.compile_manuscript( + timeout=300, # Max compilation time (seconds) + no_figs=False, # Exclude figures + no_tables=False, # Exclude tables + draft=False, # Draft mode (fast, lower quality) + dark_mode=False, # Dark color scheme + quiet=False, # Suppress output + ) + +API Reference +------------- + +.. automodule:: scitex.writer + :members: + :show-inheritance: diff --git a/src/scitex/_sphinx_html/_sources/source/cli.rst.txt b/src/scitex/_sphinx_html/_sources/source/cli.rst.txt new file mode 100644 index 000000000..aa4958a32 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/cli.rst.txt @@ -0,0 +1,98 @@ +CLI Reference +============= + +SciTeX provides a unified command-line interface. All commands follow the +pattern: + +.. code-block:: bash + + scitex [options] + +Run ``scitex --help-recursive`` to see every available command. + +General +------- + +.. code-block:: bash + + scitex --help-recursive # Show all commands across all modules + scitex list-python-apis # List all Python APIs (210 items) + scitex mcp list-tools # List all MCP tools (120+ tools) + +Scholar +------- + +Literature search, PDF management, and metadata enrichment. + +.. code-block:: bash + + scitex scholar fetch # Download paper by DOI + scitex scholar bibtex # Enrich BibTeX file with metadata + scitex scholar search # Search for papers + +Stats +----- + +Statistical testing with automatic test recommendation. + +.. code-block:: bash + + scitex stats recommend # Suggest appropriate statistical tests + scitex stats run # Run a specific test on data + +Audio +----- + +Text-to-speech and audio playback. + +.. code-block:: bash + + scitex audio speak # Convert text to speech + scitex audio play # Play an audio file + +Capture +------- + +Screen capture and monitoring. + +.. code-block:: bash + + scitex capture snap # Take a screenshot + scitex capture monitor # Start screen monitoring + +Template +-------- + +Project scaffolding from built-in templates. + +.. code-block:: bash + + scitex template clone + +Available template types: + +- ``research`` -- Research project with session tracking +- ``pip`` -- Python pip package +- ``paper`` -- LaTeX manuscript project +- ``app`` -- SciTeX app with bridge integration +- ``singularity`` -- Singularity container definition + +Introspect +---------- + +Python code introspection for exploring APIs. + +.. code-block:: bash + + scitex introspect api # List APIs for a module + scitex introspect source # View source code of a function + +Dev +--- + +Ecosystem development and version management. + +.. code-block:: bash + + scitex dev versions # Show versions of all installed packages + scitex dev ecosystem # Manage ecosystem packages diff --git a/src/scitex/_sphinx_html/_sources/source/concepts.rst.txt b/src/scitex/_sphinx_html/_sources/source/concepts.rst.txt new file mode 100644 index 000000000..8db46951a --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/concepts.rst.txt @@ -0,0 +1,267 @@ +Core Concepts +============= + +This page explains the design patterns behind SciTeX. Understanding +these patterns will help you use the toolkit effectively and extend it +for your own workflows. + + +Modular Architecture +-------------------- + +SciTeX is not a monolith. Each module is backed by an independent +package that can be installed and used on its own: + +.. code-block:: bash + + pip install scitex-io # Use standalone: import scitex_io + pip install scitex-stats # Use standalone: import scitex_stats + pip install figrecipe # Use standalone: import figrecipe + +When you install ``scitex``, these packages are available under a +unified namespace: + +.. code-block:: python + + import scitex as stx + + stx.io.save(data, "out.csv") # scitex-io + stx.stats.run_test(...) # scitex-stats + stx.plt.subplots() # figrecipe + +**Lazy loading** ensures that importing ``scitex`` does not pull in every +dependency. Modules are loaded only when first accessed. If you never +touch ``stx.scholar``, its dependencies are never imported and startup +remains fast. + +The main ``scitex`` package itself contains no runtime logic for I/O, +statistics, or plotting. It is an orchestrator: it re-exports +sub-package APIs, provides the CLI entry point, hosts the MCP server, +and owns the session decorator. + + +Session Decorator +----------------- + +The ``@stx.session`` decorator converts a plain Python function into a +reproducible, CLI-enabled experiment. + +.. code-block:: python + + import scitex as stx + + @stx.session + def main( + n_epochs=50, + learning_rate=0.001, + CONFIG=stx.session.INJECTED, + logger=stx.session.INJECTED, + ): + logger.info(f"Training for {n_epochs} epochs at lr={learning_rate}") + return 0 + + if __name__ == "__main__": + main() + +**Why it exists.** Research scripts tend to accumulate boilerplate: +argument parsing, output directory creation, logging setup, random seed +management, config file loading. The session decorator handles all of +these so that the function body contains only experiment logic. + +**What it provides:** + +1. **Automatic CLI.** Function parameters become command-line arguments. + ``n_epochs=50`` becomes ``--n-epochs 50`` with no argparse code. + +2. **Config injection.** Parameters in ``./config/*.yaml`` are aggregated + into a single ``CONFIG`` dict and injected at runtime. + +3. **Reproducibility.** Random seeds are fixed. All parameters, stdout, + and stderr are logged to the output directory. + +4. **Output organization.** Each run gets a timestamped directory under + ``script_out/``: + + .. code-block:: text + + script_out/FINISHED_SUCCESS/2026-03-18_14-30-00_AbC1/ + +-- CONFIGS/CONFIG.yaml # All parameters + +-- logs/stdout.log # Captured output + +-- logs/stderr.log # Captured errors + +Parameters marked ``stx.session.INJECTED`` are sentinel values. The +decorator replaces them with runtime objects (logger, config, plotting +backend, color palette, random generator). You never pass these +yourself -- they signal to the decorator what to inject. + + +Unified I/O +------------ + +``stx.io.save`` and ``stx.io.load`` provide a single interface for 30+ +file formats. The format is determined by the file extension: + +.. code-block:: python + + stx.io.save(dataframe, "results.csv") + stx.io.save(array, "weights.npy") + stx.io.save(fig, "figure.png") + stx.io.save(config, "params.yaml") + +The same call pattern works for every type. No need to remember +``pd.to_csv``, ``np.save``, ``fig.savefig``, or ``yaml.dump``. + +**Figure data export.** When saving a matplotlib figure, SciTeX +automatically exports the plotted data as a CSV file alongside the +image. This is a deliberate design choice: every figure in a publication +should be backed by accessible data, so that reviewers and readers can +verify the plot independently. + +.. code-block:: python + + stx.io.save(fig, "plot.png") + # Creates: plot.png (the image) + # plot.csv (the underlying data) + +This behavior is powered by figrecipe's ``RecordingFigure``, which +tracks all data passed to plotting calls and serializes it on save. + + +Three Interfaces +----------------- + +Every SciTeX feature is accessible through three interfaces: + +1. **Python API** -- for scripts and notebooks +2. **CLI** -- for shell workflows and automation +3. **MCP** -- for AI agents via the Model Context Protocol + +For example, running a statistical test: + +.. code-block:: python + + # Python API + result = stx.stats.run_test("ttest_ind", group1, group2) + +.. code-block:: bash + + # CLI + scitex stats run ttest_ind --data data.csv + +.. code-block:: text + + # MCP tool (called by AI agents) + stats_run_test(test="ttest_ind", data=[[1,2,3],[4,5,6]]) + +This three-interface design means that any workflow a human builds in +Python can be replicated by an AI agent through MCP, or scripted in a +shell pipeline through the CLI. The interfaces share the same +underlying implementation, so behavior is identical across all three. + +The MCP server aggregates 120+ tools from all sub-packages. AI agents +can discover available tools, read documentation, and execute full +research pipelines -- from literature search to manuscript compilation -- +without human intervention. + + +Delegation Pattern +------------------- + +The ``scitex`` package is intentionally thin. It delegates all domain +logic to standalone packages: + +.. list-table:: + :header-rows: 1 + :widths: 25 25 50 + + * - scitex module + - Standalone package + - Responsibility + * - ``stx.io`` + - scitex-io + - File I/O (30+ formats) + * - ``stx.stats`` + - scitex-stats + - Statistical testing (23 tests) + * - ``stx.plt`` + - figrecipe + - Publication-ready figures + * - ``stx.writer`` + - scitex-writer + - LaTeX manuscript compilation + * - ``stx.scholar`` + - scitex-scholar + - Literature search, PDF download + * - ``stx.dataset`` + - scitex-dataset + - Scientific dataset access + +**Why delegation?** Each sub-package has its own release cycle, test +suite, and dependency tree. A bug fix in ``scitex-io`` does not require +releasing all of ``scitex``. Users who only need file I/O can +``pip install scitex-io`` without pulling in matplotlib, LaTeX tooling, +or audio dependencies. + +From the user's perspective, the delegation is invisible. A single +``import scitex as stx`` provides access to everything. The lazy loader +in ``scitex.__init__`` resolves ``stx.io`` to the ``scitex_io`` package +on first access. + + +Configuration +-------------- + +SciTeX uses a layered configuration system: + +**Project-level config (YAML files)** + +Place YAML files in ``./config/`` at your project root. The session +decorator aggregates all files in this directory into a single ``CONFIG`` +dict: + +.. code-block:: text + + config/ + +-- model.yaml # {"hidden_size": 256, "n_layers": 4} + +-- training.yaml # {"epochs": 100, "batch_size": 32} + +.. code-block:: python + + @stx.session + def main(CONFIG=stx.session.INJECTED): + print(CONFIG["hidden_size"]) # 256 + print(CONFIG["epochs"]) # 100 + +**Environment-level config (.env.d/)** + +Credentials, API keys, and machine-specific paths live in ``.env.d/``: + +.. code-block:: text + + .env.d/ + +-- entry.src # Single entry point (source this) + +-- 00_scitex.env # Base paths + +-- 01_scholar.env # OpenAthens credentials + +-- 01_audio.env # TTS backend config + +Source the entry point in your shell profile: + +.. code-block:: bash + + # In ~/.bashrc or ~/.zshrc + source /path/to/.env.d/entry.src + +**MCP environment (.src files)** + +For AI agent deployments, a single ``.src`` file bundles all environment +variables. The MCP server loads it at startup via ``SCITEX_ENV_SRC``: + +.. code-block:: bash + + export SCITEX_ENV_SRC=~/.scitex/scitex/local.src + +This keeps the ``.mcp.json`` configuration static across machines -- +only the ``.src`` file changes between local and remote environments. + +The configuration hierarchy is: CLI arguments override YAML config, +which overrides environment variables, which override built-in defaults. diff --git a/src/scitex/_sphinx_html/_sources/source/ecosystem.rst.txt b/src/scitex/_sphinx_html/_sources/source/ecosystem.rst.txt new file mode 100644 index 000000000..a4d47e881 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/ecosystem.rst.txt @@ -0,0 +1,123 @@ +Ecosystem +========= + +SciTeX is a modular toolkit composed of standalone packages. Each package +can be installed and used independently, or accessed through the unified +``import scitex`` interface. + +Delegation Pattern +------------------ + +The ``scitex`` package itself contains no runtime logic. It acts as an +orchestrator that re-exports functionality from sub-packages via lazy +imports. When you write ``scitex.io.load("data.csv")``, the call is +delegated to the ``scitex_io`` package under the hood. + +This means: + +- **Standalone use**: ``pip install scitex-io`` then ``import scitex_io`` +- **Unified use**: ``pip install scitex[io]`` then ``import scitex; scitex.io.load(...)`` + +Both paths execute the same code. The ``scitex`` package simply provides +the namespace glue. + +.. code-block:: python + + # These are equivalent: + import scitex_io + scitex_io.load("data.csv") + + import scitex + scitex.io.load("data.csv") + +Package Reference +----------------- + +.. list-table:: + :header-rows: 1 + :widths: 20 18 22 40 + + * - Package + - scitex Module + - PyPI + - Description + * - scitex-io + - ``scitex.io`` + - ``pip install scitex-io`` + - Unified file I/O for 30+ formats + * - scitex-stats + - ``scitex.stats`` + - ``pip install scitex-stats`` + - Publication-ready statistics (23+ tests) + * - figrecipe + - ``scitex.plt`` + - ``pip install figrecipe`` + - Publication-ready matplotlib figures + * - scitex-writer + - ``scitex.writer`` + - ``pip install scitex-writer`` + - LaTeX manuscript compilation + * - scitex-scholar + - ``scitex.scholar`` + - ``pip install scitex-scholar`` + - Literature search and management + * - scitex-audio + - ``scitex.audio`` + - ``pip install scitex-audio`` + - Text-to-speech and audio + * - scitex-dev + - ``scitex.dev`` + - ``pip install scitex-dev`` + - Developer tools, ecosystem management + * - scitex-clew + - ``scitex.clew`` + - ``pip install scitex-clew`` + - Hash-based reproducibility verification + * - scitex-linter + - ``scitex.linter`` + - ``pip install scitex-linter`` + - AST-based code pattern checking + * - scitex-dataset + - ``scitex.dataset`` + - ``pip install scitex-dataset`` + - Scientific dataset access (DANDI, OpenNeuro, PhysioNet) + * - crossref-local + - ``scitex.scholar.crossref`` + - ``pip install crossref-local`` + - Local CrossRef database (167M+ papers) + * - openalex-local + - ``scitex.scholar.openalex`` + - ``pip install openalex-local`` + - Local OpenAlex database (250M+ papers) + * - socialia + - ``scitex.social`` + - ``pip install socialia`` + - Social media posting (Twitter, LinkedIn) + * - scitex-app + - ``scitex.app`` + - ``pip install scitex-app`` + - Runtime SDK for SciTeX apps + * - scitex-cloud + - ``scitex.cloud`` + - ``pip install scitex-cloud`` + - Cloud platform integration + * - scitex-notification + - ``scitex.notify`` + - ``pip install scitex-notification`` + - Multi-backend notifications + +Installation +------------ + +Install individual packages or use extras through the main package: + +.. code-block:: bash + + # Full installation (all packages) + pip install scitex[all] + + # Typical research setup + pip install scitex[plt,stats,scholar] + + # Individual standalone package + pip install figrecipe diff --git a/src/scitex/_sphinx_html/_sources/source/gallery.rst.txt b/src/scitex/_sphinx_html/_sources/source/gallery.rst.txt new file mode 100644 index 000000000..9afe7c776 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/gallery.rst.txt @@ -0,0 +1,206 @@ +Plot Gallery +============ + +``stx.plt`` wraps matplotlib axes with data-tracking methods. +All ``plot_*`` methods record data for automatic CSV export. + +Generate the full gallery locally: + +.. code-block:: python + + import scitex as stx + stx.plt.gallery.generate(output_dir="./gallery") + +Line Plots +---------- + +.. list-table:: + :widths: 50 50 + + * - .. figure:: _static/gallery/line/stx_line.png + :width: 100% + + ``ax.plot_line(x, y)`` + + - .. figure:: _static/gallery/line/stx_shaded_line.png + :width: 100% + + ``ax.plot_shaded_line(x, y_mean, y_std)`` + + * - .. figure:: _static/gallery/line/plot.png + :width: 100% + + ``ax.plot(x, y)`` (standard matplotlib) + + - .. figure:: _static/gallery/line/step.png + :width: 100% + + ``ax.step(x, y)`` + +Statistical Plots +----------------- + +.. list-table:: + :widths: 50 50 + + * - .. figure:: _static/gallery/statistical/stx_mean_std.png + :width: 100% + + ``ax.plot_mean_std(groups)`` + + - .. figure:: _static/gallery/statistical/stx_mean_ci.png + :width: 100% + + ``ax.plot_mean_ci(groups)`` + + * - .. figure:: _static/gallery/statistical/stx_median_iqr.png + :width: 100% + + ``ax.plot_median_iqr(groups)`` + + - .. figure:: _static/gallery/statistical/stx_errorbar.png + :width: 100% + + ``ax.plot_errorbar(x, y, yerr)`` + +Distribution Plots +------------------ + +.. list-table:: + :widths: 50 50 + + * - .. figure:: _static/gallery/distribution/stx_kde.png + :width: 100% + + ``ax.plot_kde(data)`` + + - .. figure:: _static/gallery/distribution/stx_ecdf.png + :width: 100% + + ``ax.plot_ecdf(data)`` + + * - .. figure:: _static/gallery/distribution/stx_joyplot.png + :width: 100% + + ``ax.plot_joyplot(groups)`` + + - .. figure:: _static/gallery/distribution/hist.png + :width: 100% + + ``ax.hist(data)`` + +Categorical Plots +----------------- + +.. list-table:: + :widths: 50 50 + + * - .. figure:: _static/gallery/categorical/stx_violin.png + :width: 100% + + ``ax.plot_violin(groups)`` + + - .. figure:: _static/gallery/categorical/stx_box.png + :width: 100% + + ``ax.plot_box(groups)`` + + * - .. figure:: _static/gallery/categorical/stx_bar.png + :width: 100% + + ``ax.plot_bar(labels, values)`` + + - .. figure:: _static/gallery/categorical/stx_barh.png + :width: 100% + + ``ax.plot_barh(labels, values)`` + +Scatter Plots +------------- + +.. list-table:: + :widths: 50 50 + + * - .. figure:: _static/gallery/scatter/stx_scatter.png + :width: 100% + + ``ax.plot_scatter(x, y)`` + + - .. figure:: _static/gallery/scatter/scatter.png + :width: 100% + + ``ax.scatter(x, y)`` (standard matplotlib) + +Heatmaps and Grids +------------------- + +.. list-table:: + :widths: 50 50 + + * - .. figure:: _static/gallery/grid/stx_heatmap.png + :width: 100% + + ``ax.plot_heatmap(matrix)`` + + - .. figure:: _static/gallery/grid/stx_conf_mat.png + :width: 100% + + ``ax.plot_conf_mat(cm)`` + + * - .. figure:: _static/gallery/grid/stx_image.png + :width: 100% + + ``ax.plot_image(img)`` + + - .. figure:: _static/gallery/grid/stx_imshow.png + :width: 100% + + ``ax.plot_imshow(data)`` + +Area Plots +---------- + +.. list-table:: + :widths: 50 50 + + * - .. figure:: _static/gallery/area/stx_fill_between.png + :width: 100% + + ``ax.plot_fill_between(x, y1, y2)`` + + - .. figure:: _static/gallery/area/stx_fillv.png + :width: 100% + + ``ax.plot_fillv(x_start, x_end)`` + +Special Plots +------------- + +.. list-table:: + :widths: 50 50 + + * - .. figure:: _static/gallery/special/stx_raster.png + :width: 100% + + ``ax.plot_raster(spike_times)`` + + - .. figure:: _static/gallery/special/stx_rectangle.png + :width: 100% + + ``ax.plot_rectangle(xy, w, h)`` + +Contour and Vector Plots +------------------------- + +.. list-table:: + :widths: 50 50 + + * - .. figure:: _static/gallery/contour/stx_contour.png + :width: 100% + + ``ax.plot_contour(x, y, z)`` + + - .. figure:: _static/gallery/vector/quiver.png + :width: 100% + + ``ax.quiver(x, y, u, v)`` diff --git a/src/scitex/_sphinx_html/_sources/source/index.rst.txt b/src/scitex/_sphinx_html/_sources/source/index.rst.txt new file mode 100644 index 000000000..9a5d505de --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/index.rst.txt @@ -0,0 +1,88 @@ +SciTeX Documentation +==================== + +SciTeX is a modular Python toolkit for research automation. It provides a +unified interface across the full scientific workflow: from data loading and +statistical analysis, through publication-ready plotting and manuscript +compilation, to AI-assisted literature review. Every module is installable +independently, and the entire surface area is exposed to AI agents through +an MCP (Model Context Protocol) server. + +Key Features +------------ + +- **Session decorator** -- wrap any experiment function with ``@stx.session`` + to get automatic directory management, reproducibility logging, and + error-safe cleanup. + +- **Unified I/O** -- ``stx.io.save`` / ``stx.io.load`` handle 40+ formats + (CSV, HDF5, YAML, images, PDFs, ...) through a single call. + +- **Statistics** -- ``stx.stats`` provides hypothesis testing, effect sizes, + power analysis, and multiple-comparison correction with APA-formatted + output. + +- **Plotting** -- ``stx.plt`` produces publication-ready figures via + `figrecipe `_, with + millimetre-based layouts, journal style presets, and automatic CSV data + export alongside every saved figure. + +- **Manuscript writing** -- ``stx.writer`` compiles LaTeX manuscripts, + manages BibTeX, and exports to Overleaf. + +- **Literature management** -- ``stx.scholar`` searches CrossRef, OpenAlex, + and Google Scholar; downloads and parses PDFs; and maintains a local + citation database. + +- **MCP for AI agents** -- every capability above is available as a + tool call through the built-in MCP server, so LLM agents can run + statistics, create figures, and compile papers programmatically. + +Getting Started +--------------- + +1. :doc:`installation` -- install the core package and choose the extras you + need. +2. :doc:`quickstart` -- a five-minute walkthrough of the session decorator, + I/O, and plotting. +3. :doc:`api/index` -- full API reference generated from docstrings. + + +.. toctree:: + :maxdepth: 2 + :caption: Getting Started + + installation + quickstart + + +.. toctree:: + :maxdepth: 2 + :caption: Guides + + concepts + gallery + ecosystem + + +.. toctree:: + :maxdepth: 2 + :caption: Interfaces + + cli + mcp + + +.. toctree:: + :maxdepth: 2 + :caption: Reference + + api/index + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/src/scitex/_sphinx_html/_sources/source/installation.rst.txt b/src/scitex/_sphinx_html/_sources/source/installation.rst.txt new file mode 100644 index 000000000..5b692ce9f --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/installation.rst.txt @@ -0,0 +1,166 @@ +Installation +============ + +Requirements +------------ + +- **Python 3.10+** +- ``uv`` (strongly recommended) — install with ``pip install uv`` or + ``curl -LsSf https://astral.sh/uv/install.sh | sh`` +- ``pip`` 21+ also works, but expect 30–90 min for ``scitex[all]`` + (see warning below) + + +.. warning:: + + ``pip install "scitex[all]"`` typically takes **30–90 minutes** because + pip's serial resolver walks version histories of the large transitive + dependency set (numpy/pandas/torch/jax/playwright/openalex-local/…). + Use **uv** instead — it resolves the same set in parallel in **1–3 + minutes**. Every ``pip install`` line on this page also works as + ``uv pip install`` and we recommend the uv form. + + +Core Install +------------ + +The base package pulls in only lightweight dependencies and gives you access +to session management, path utilities, string helpers, and the module +discovery system. + +.. code-block:: bash + + uv pip install scitex # recommended + pip install scitex # also works, slower for [all] + + +Recommended: Install Everything +------------------------------- + +If you want the full experience (plotting, statistics, I/O, scholar, writer, +and every other module): + +.. code-block:: bash + + uv pip install "scitex[all]" # ~3 min + pip install "scitex[all]" # ~30–90 min (resolver thrash) + + +Research Workflow +----------------- + +For a typical research project you need figures, statistics, and literature +search but not audio, browser automation, or cloud tools: + +.. code-block:: bash + + pip install "scitex[plt,stats,scholar]" + + +Per-Module Extras +----------------- + +Install only what you need. Each extra maps to a self-contained capability. + +.. list-table:: + :header-rows: 1 + :widths: 15 55 + + * - Extra + - Description + * - ``plt`` + - Publication-ready figures via figrecipe (matplotlib, seaborn, Pillow) + * - ``stats`` + - Hypothesis testing, effect sizes, power analysis (scitex-stats, scipy, statsmodels) + * - ``io`` + - Unified I/O for 40+ formats (HDF5, Excel, YAML, PDF, images, ...) + * - ``scholar`` + - Literature search and PDF management (CrossRef, OpenAlex, Semantic Scholar) + * - ``writer`` + - LaTeX manuscript compilation, BibTeX management, Overleaf export + * - ``audio`` + - Text-to-speech and audio utilities (scitex-audio) + * - ``ai`` + - LLM APIs (OpenAI, Anthropic, Google, Groq) and ML tools (scikit-learn) + * - ``browser`` + - Web automation via Playwright + * - ``capture`` + - Screenshot capture (mss, Playwright) + * - ``dataset`` + - Scientific dataset access (DANDI, OpenNeuro, PhysioNet) + * - ``cloud`` + - Cloud integration utilities + * - ``app`` + - Unified file storage SDK (scitex-app) + * - ``session`` + - Session decorator with reproducibility logging + * - ``diagram`` + - Diagram generation (Mermaid, Graphviz) + * - ``db`` + - Database access (SQLAlchemy, PostgreSQL) + * - ``cv`` + - Computer vision (OpenCV, Pillow) + * - ``dsp`` + - Digital signal processing (scipy, tensorpac) + * - ``social`` + - Social media posting (socialia) + * - ``tunnel`` + - SSH tunnel management (scitex-tunnel) + * - ``all`` + - Everything above + + +Example combinations: + +.. code-block:: bash + + # Neuroscience analysis + pip install "scitex[plt,stats,dsp,dataset]" + + # Paper writing + pip install "scitex[writer,scholar,plt]" + + # AI agent development + pip install "scitex[ai,browser,capture]" + + +Development Install +------------------- + +Clone the repository and install in editable mode with development tools: + +.. code-block:: bash + + git clone https://github.com/ywatanabe1989/scitex-python.git + cd scitex-python + pip install -e ".[dev]" + +The ``dev`` extra includes pytest, ruff, mypy, Sphinx, and build tools. + +Using uv (recommended) +^^^^^^^^^^^^^^^^^^^^^^^ + +`uv `_ resolves and installs dependencies +in parallel from a Rust resolver — roughly 10–30× faster than pip on +``scitex[all]`` (3 min vs. 30–90 min). Install it once with +``pip install uv`` (or the standalone shell installer), then prefix +every install command on this page with ``uv``: + +.. code-block:: bash + + uv pip install -e ".[dev]" + + +Verifying the Installation +-------------------------- + +.. code-block:: python + + import scitex as stx + print(stx.__version__) + +To check which optional modules are available: + +.. code-block:: python + + stx.usage.list() diff --git a/src/scitex/_sphinx_html/_sources/source/mcp.rst.txt b/src/scitex/_sphinx_html/_sources/source/mcp.rst.txt new file mode 100644 index 000000000..429480610 --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/mcp.rst.txt @@ -0,0 +1,152 @@ +MCP Server +========== + +What is MCP? +------------ + +The `Model Context Protocol `_ (MCP) is +an open protocol that lets AI agents call external tools through a +standardized interface. Instead of generating code and hoping it works, an +agent can invoke a well-defined tool and receive structured results. + +SciTeX implements an MCP server that exposes 120+ tools covering the full +research workflow. Any MCP-compatible client -- Claude Code, Cursor, or +custom agents -- can use these tools to conduct literature searches, run +statistics, create figures, and compile manuscripts. + +Setup +----- + +Add the following to ``.mcp.json`` in your project root: + +.. code-block:: json + + { + "mcpServers": { + "scitex": { + "command": "scitex", + "args": ["mcp", "start"], + "env": { + "SCITEX_ENV_SRC": "${SCITEX_ENV_SRC}" + } + } + } + } + +Then set the environment source file in your shell profile: + +.. code-block:: bash + + # Local machine + export SCITEX_ENV_SRC=~/.scitex/scitex/local.src + + # Remote server + export SCITEX_ENV_SRC=~/.scitex/scitex/remote.src + +Generate a template ``.src`` file: + +.. code-block:: bash + + scitex env-template -o ~/.scitex/scitex/local.src + +Or install the MCP server globally: + +.. code-block:: bash + + scitex mcp installation + +Tool Categories +--------------- + +.. list-table:: + :header-rows: 1 + :widths: 20 10 70 + + * - Category + - Tools + - Description + * - writer + - 28 + - LaTeX manuscript compilation + * - scholar + - 23 + - PDF download, metadata enrichment + * - capture + - 12 + - Screen monitoring and capture + * - introspect + - 12 + - Python code introspection + * - audio + - 10 + - Text-to-speech, audio playback + * - stats + - 10 + - Automated statistical testing + * - plt + - 9 + - Matplotlib figure creation + * - diagram + - 9 + - Mermaid and Graphviz diagrams + * - dataset + - 8 + - Scientific dataset access + * - social + - 7 + - Social media posting + * - canvas + - 7 + - Scientific figure canvas + * - template + - 6 + - Project scaffolding + * - verify + - 6 + - Reproducibility verification + * - dev + - 6 + - Ecosystem version management + * - ui + - 5 + - Notifications + * - linter + - 3 + - Code pattern checking + +All tools accept JSON parameters and return structured results. + +Example Workflow +---------------- + +A typical AI-driven research workflow chains tools across categories: + +1. **Scholar** -- Search literature and download papers + + .. code-block:: text + + scholar_search_papers(query="neural oscillations gamma band") + scholar_fetch_papers(dois=["10.1038/s41586-024-..."]) + +2. **Stats** -- Analyze experimental data + + .. code-block:: text + + stats_recommend_tests(data=[[1.2, 3.4, ...], [2.1, 4.5, ...]]) + stats_run_test(test="mann_whitney_u", groups=[[...], [...]]) + +3. **Plt** -- Create publication-ready figures + + .. code-block:: text + + plt_bar(data={"Control": [1,2,3], "Treatment": [4,5,6]}, + ylabel="Response", title="Figure 1") + +4. **Writer** -- Compile the manuscript + + .. code-block:: text + + writer_compile_manuscript(project="my_paper") + +The agent orchestrates these steps autonomously, passing outputs from one +tool as inputs to the next. diff --git a/src/scitex/_sphinx_html/_sources/source/quickstart.rst.txt b/src/scitex/_sphinx_html/_sources/source/quickstart.rst.txt new file mode 100644 index 000000000..a1952758f --- /dev/null +++ b/src/scitex/_sphinx_html/_sources/source/quickstart.rst.txt @@ -0,0 +1,287 @@ +Quickstart +========== + +This guide covers the most common SciTeX workflows with working code +examples. For design rationale, see :doc:`concepts`. + + +Session Decorator +----------------- + +The ``@stx.session`` decorator is the recommended entry point for any +SciTeX script. It converts your function into a self-contained, +reproducible experiment with automatic CLI generation, config loading, +logging, and organized output directories. + +.. code-block:: python + + import scitex as stx + + @stx.session + def main( + n_samples=100, + CONFIG=stx.session.INJECTED, + plt=stx.session.INJECTED, + logger=stx.session.INJECTED, + ): + """Generate sample data and plot.""" + import numpy as np + + x = np.linspace(0, 2 * np.pi, n_samples) + y = np.sin(x) + + fig, ax = stx.plt.subplots() + ax.plot_line(x, y) + ax.set_xyt("Time", "Amplitude", "Sine Wave") + stx.io.save(fig, "sine.png") # Saves sine.png + sine.csv + return 0 + + if __name__ == "__main__": + main() + +Run from the command line -- arguments are generated automatically from +the function signature: + +.. code-block:: bash + + python script.py --n-samples 200 + +Parameters marked with ``stx.session.INJECTED`` are provided by the +session runtime. You never pass them yourself: + +- **CONFIG** -- aggregated YAML parameters from ``./config/*.yaml`` +- **plt** -- matplotlib wrapped by figrecipe +- **logger** -- colored, file-backed logger +- **COLORS** -- publication color palette +- **rngg** -- seeded random number generator + +Output is organized into a timestamped directory: + +.. code-block:: text + + script_out/FINISHED_SUCCESS/2026-03-18_14-30-00_AbC1/ + +-- sine.png # Figure with embedded metadata + +-- sine.csv # Auto-exported plot data + +-- CONFIGS/CONFIG.yaml # Reproducible parameters + +-- logs/stdout.log # Standard output + +-- logs/stderr.log # Standard error + + +Unified File I/O +----------------- + +``stx.io.save`` and ``stx.io.load`` handle 30+ formats through a single +interface. The format is inferred from the file extension. + +.. code-block:: python + + import scitex as stx + import numpy as np + import pandas as pd + + # DataFrame -- CSV + df = pd.DataFrame({"x": [1, 2, 3], "y": [4, 5, 6]}) + stx.io.save(df, "data.csv") + df = stx.io.load("data.csv") + + # NumPy array -- NPY + arr = np.random.randn(100, 50) + stx.io.save(arr, "array.npy") + arr = stx.io.load("array.npy") + + # Figure -- PNG (also generates CSV of plotted data) + fig, ax = stx.plt.subplots() + ax.plot_line([1, 2, 3, 4, 5]) + stx.io.save(fig, "plot.png") # Creates plot.png AND plot.csv + + # Dictionary -- YAML + params = {"learning_rate": 0.001, "epochs": 100} + stx.io.save(params, "params.yaml") + params = stx.io.load("params.yaml") + + # Dictionary -- JSON + metadata = {"subject": "S01", "task": "rest"} + stx.io.save(metadata, "meta.json") + metadata = stx.io.load("meta.json") + + # Arbitrary object -- Pickle + stx.io.save({"complex": [1, 2, 3]}, "data.pkl") + obj = stx.io.load("data.pkl") + +When saving a matplotlib figure, SciTeX automatically exports the +underlying data as a CSV file alongside the image. This ensures every +figure in a paper can be independently verified and reproduced. + + +Statistical Analysis +-------------------- + +SciTeX wraps 23 statistical tests with a consistent interface that +includes effect sizes, confidence intervals, and power analysis. + +.. code-block:: python + + import scitex as stx + import numpy as np + + group1 = np.random.randn(30) + 0.5 + group2 = np.random.randn(30) + + # Run a t-test with full reporting + result = stx.stats.run_test( + "ttest_ind", group1, group2, return_as="dataframe" + ) + print(result) + # Columns: test, statistic, p_value, effect_size, ci_lower, ci_upper, power + +Not sure which test to use? Let SciTeX recommend one: + +.. code-block:: python + + data = {"group_a": group1, "group_b": group2} + recommendations = stx.stats.recommend_tests(data) + print(recommendations) + +Additional utilities: + +.. code-block:: python + + # Multiple comparison correction + corrected = stx.stats.correct_pvalues([0.01, 0.04, 0.06], method="bonferroni") + + # Effect size calculation + d = stx.stats.effect_size(group1, group2, test="cohens_d") + + # Format results for publication + formatted = stx.stats.format_results(result) + # "t(58) = 2.34, p = .021, d = 0.60" + + +Publication-Ready Figures +------------------------- + +SciTeX delegates to `figrecipe `_ +for publication-quality matplotlib figures with a consistent API. + +**Basic line plot** + +.. code-block:: python + + import scitex as stx + import numpy as np + + x = np.linspace(0, 10, 200) + + fig, ax = stx.plt.subplots() + ax.plot_line(x, np.sin(x)) + ax.set_xyt("Time (s)", "Amplitude", "Sine Wave") + stx.io.save(fig, "line.png") + +**Multi-panel figure** + +.. code-block:: python + + import scitex as stx + import numpy as np + + fig, axes = stx.plt.subplots(1, 3) + + # Panel A: line plot + x = np.linspace(0, 10, 200) + axes[0].plot_line(x, np.sin(x)) + axes[0].set_xyt("Time", "Value", "Line") + + # Panel B: violin plot + data = [np.random.randn(50) + i for i in range(3)] + axes[1].stx_violin(data) + axes[1].set_xyt("Group", "Value", "Violin") + + # Panel C: heatmap + matrix = np.random.randn(10, 10) + axes[2].stx_heatmap(matrix) + axes[2].set_xyt("X", "Y", "Heatmap") + + stx.io.save(fig, "panels.png") + +**Statistical visualization** + +.. code-block:: python + + import scitex as stx + import numpy as np + + data = np.random.randn(100, 50) + + fig, ax = stx.plt.subplots() + ax.stx_mean_std(data) + ax.set_xyt("Time", "Value", "Mean +/- SD") + + stx.io.save(fig, "stats_plot.png") + + +Literature Management +--------------------- + +The scholar module handles paper discovery, PDF downloads, and BibTeX +enrichment. + +**CLI usage** + +.. code-block:: bash + + # Enrich a BibTeX file with missing metadata (DOIs, abstracts) + scitex scholar bibtex refs.bib + + # Fetch a paper by DOI + scitex scholar fetch "10.1038/s41586-024-07487-w" + + # Search for papers + scitex scholar search "deep learning EEG" --limit 20 + +**Python API** + +.. code-block:: python + + import scitex as stx + + # Search for papers + results = stx.scholar.search("transformer attention mechanism", limit=10) + + # Parse and enrich a BibTeX file + entries = stx.scholar.parse_bibtex("refs.bib") + enriched = stx.scholar.enrich_bibtex("refs.bib") + + +CLI Usage +--------- + +SciTeX provides a unified CLI that mirrors the Python module structure. + +.. code-block:: bash + + # Show all available commands + scitex --help-recursive + + # Statistics + scitex stats recommend # Suggest tests for your data + scitex stats run ttest_ind # Run a specific test + + # Scholar / Literature + scitex scholar fetch "10.1038/..." # Download paper by DOI + scitex scholar bibtex refs.bib # Enrich BibTeX metadata + scitex scholar search "query" # Search for papers + + # Figures and diagrams + scitex plt info # Show available plot types + + # Utilities + scitex audio speak "Analysis complete" # Text-to-speech notification + scitex capture snap # Take a screenshot + + # Introspection + scitex introspect api scitex.stats # List APIs for a module + scitex list-python-apis # List all Python APIs + scitex mcp list-tools # List all MCP tools + +Every CLI command corresponds to a Python function and an MCP tool. +See :doc:`concepts` for details on this three-interface design. diff --git a/src/scitex/_sphinx_html/_static/_sphinx_javascript_frameworks_compat.js b/src/scitex/_sphinx_html/_static/_sphinx_javascript_frameworks_compat.js new file mode 100644 index 000000000..81415803e --- /dev/null +++ b/src/scitex/_sphinx_html/_static/_sphinx_javascript_frameworks_compat.js @@ -0,0 +1,123 @@ +/* Compatability shim for jQuery and underscores.js. + * + * Copyright Sphinx contributors + * Released under the two clause BSD licence + */ + +/** + * small helper function to urldecode strings + * + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL + */ +jQuery.urldecode = function(x) { + if (!x) { + return x + } + return decodeURIComponent(x.replace(/\+/g, ' ')); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} diff --git a/src/scitex/_sphinx_html/_static/architecture.mmd b/src/scitex/_sphinx_html/_static/architecture.mmd new file mode 100644 index 000000000..54ad1616a --- /dev/null +++ b/src/scitex/_sphinx_html/_static/architecture.mmd @@ -0,0 +1,35 @@ +%%{init: {"theme": "default"}}%% +graph LR + subgraph Experiment + session["session"] + config["config"] + io["io"] + logging["logging"] + repro["repro"] + end + + subgraph Analysis & Visualization + stats["stats"] + dsp["dsp"] + plt["plt"] + diagram["diagram"] + end + + subgraph Publication + scholar["scholar"] + writer["writer"] + clew["clew"] + end + + style session fill:#e8f4fd,stroke:#2196F3,stroke-width:2px + style config fill:#e8f4fd,stroke:#2196F3 + style io fill:#e8f4fd,stroke:#2196F3 + style logging fill:#e8f4fd,stroke:#2196F3 + style repro fill:#e8f4fd,stroke:#2196F3 + style stats fill:#fff3e0,stroke:#FF9800 + style dsp fill:#fff3e0,stroke:#FF9800 + style plt fill:#fff3e0,stroke:#FF9800 + style diagram fill:#fff3e0,stroke:#FF9800 + style scholar fill:#f3e5f5,stroke:#9C27B0 + style writer fill:#f3e5f5,stroke:#9C27B0 + style clew fill:#f3e5f5,stroke:#9C27B0 diff --git a/src/scitex/_sphinx_html/_static/base-stemmer.js b/src/scitex/_sphinx_html/_static/base-stemmer.js new file mode 100644 index 000000000..e6fa0c492 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/base-stemmer.js @@ -0,0 +1,476 @@ +// @ts-check + +/**@constructor*/ +BaseStemmer = function() { + /** @protected */ + this.current = ''; + this.cursor = 0; + this.limit = 0; + this.limit_backward = 0; + this.bra = 0; + this.ket = 0; + + /** + * @param {string} value + */ + this.setCurrent = function(value) { + this.current = value; + this.cursor = 0; + this.limit = this.current.length; + this.limit_backward = 0; + this.bra = this.cursor; + this.ket = this.limit; + }; + + /** + * @return {string} + */ + this.getCurrent = function() { + return this.current; + }; + + /** + * @param {BaseStemmer} other + */ + this.copy_from = function(other) { + /** @protected */ + this.current = other.current; + this.cursor = other.cursor; + this.limit = other.limit; + this.limit_backward = other.limit_backward; + this.bra = other.bra; + this.ket = other.ket; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.in_grouping = function(s, min, max) { + /** @protected */ + if (this.cursor >= this.limit) return false; + var ch = this.current.charCodeAt(this.cursor); + if (ch > max || ch < min) return false; + ch -= min; + if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return false; + this.cursor++; + return true; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.go_in_grouping = function(s, min, max) { + /** @protected */ + while (this.cursor < this.limit) { + var ch = this.current.charCodeAt(this.cursor); + if (ch > max || ch < min) + return true; + ch -= min; + if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) + return true; + this.cursor++; + } + return false; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.in_grouping_b = function(s, min, max) { + /** @protected */ + if (this.cursor <= this.limit_backward) return false; + var ch = this.current.charCodeAt(this.cursor - 1); + if (ch > max || ch < min) return false; + ch -= min; + if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return false; + this.cursor--; + return true; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.go_in_grouping_b = function(s, min, max) { + /** @protected */ + while (this.cursor > this.limit_backward) { + var ch = this.current.charCodeAt(this.cursor - 1); + if (ch > max || ch < min) return true; + ch -= min; + if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return true; + this.cursor--; + } + return false; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.out_grouping = function(s, min, max) { + /** @protected */ + if (this.cursor >= this.limit) return false; + var ch = this.current.charCodeAt(this.cursor); + if (ch > max || ch < min) { + this.cursor++; + return true; + } + ch -= min; + if ((s[ch >>> 3] & (0X1 << (ch & 0x7))) == 0) { + this.cursor++; + return true; + } + return false; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.go_out_grouping = function(s, min, max) { + /** @protected */ + while (this.cursor < this.limit) { + var ch = this.current.charCodeAt(this.cursor); + if (ch <= max && ch >= min) { + ch -= min; + if ((s[ch >>> 3] & (0X1 << (ch & 0x7))) != 0) { + return true; + } + } + this.cursor++; + } + return false; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.out_grouping_b = function(s, min, max) { + /** @protected */ + if (this.cursor <= this.limit_backward) return false; + var ch = this.current.charCodeAt(this.cursor - 1); + if (ch > max || ch < min) { + this.cursor--; + return true; + } + ch -= min; + if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) { + this.cursor--; + return true; + } + return false; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.go_out_grouping_b = function(s, min, max) { + /** @protected */ + while (this.cursor > this.limit_backward) { + var ch = this.current.charCodeAt(this.cursor - 1); + if (ch <= max && ch >= min) { + ch -= min; + if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) != 0) { + return true; + } + } + this.cursor--; + } + return false; + }; + + /** + * @param {string} s + * @return {boolean} + */ + this.eq_s = function(s) + { + /** @protected */ + if (this.limit - this.cursor < s.length) return false; + if (this.current.slice(this.cursor, this.cursor + s.length) != s) + { + return false; + } + this.cursor += s.length; + return true; + }; + + /** + * @param {string} s + * @return {boolean} + */ + this.eq_s_b = function(s) + { + /** @protected */ + if (this.cursor - this.limit_backward < s.length) return false; + if (this.current.slice(this.cursor - s.length, this.cursor) != s) + { + return false; + } + this.cursor -= s.length; + return true; + }; + + /** + * @param {Among[]} v + * @return {number} + */ + this.find_among = function(v) + { + /** @protected */ + var i = 0; + var j = v.length; + + var c = this.cursor; + var l = this.limit; + + var common_i = 0; + var common_j = 0; + + var first_key_inspected = false; + + while (true) + { + var k = i + ((j - i) >>> 1); + var diff = 0; + var common = common_i < common_j ? common_i : common_j; // smaller + // w[0]: string, w[1]: substring_i, w[2]: result, w[3]: function (optional) + var w = v[k]; + var i2; + for (i2 = common; i2 < w[0].length; i2++) + { + if (c + common == l) + { + diff = -1; + break; + } + diff = this.current.charCodeAt(c + common) - w[0].charCodeAt(i2); + if (diff != 0) break; + common++; + } + if (diff < 0) + { + j = k; + common_j = common; + } + else + { + i = k; + common_i = common; + } + if (j - i <= 1) + { + if (i > 0) break; // v->s has been inspected + if (j == i) break; // only one item in v + + // - but now we need to go round once more to get + // v->s inspected. This looks messy, but is actually + // the optimal approach. + + if (first_key_inspected) break; + first_key_inspected = true; + } + } + do { + var w = v[i]; + if (common_i >= w[0].length) + { + this.cursor = c + w[0].length; + if (w.length < 4) return w[2]; + var res = w[3](this); + this.cursor = c + w[0].length; + if (res) return w[2]; + } + i = w[1]; + } while (i >= 0); + return 0; + }; + + // find_among_b is for backwards processing. Same comments apply + /** + * @param {Among[]} v + * @return {number} + */ + this.find_among_b = function(v) + { + /** @protected */ + var i = 0; + var j = v.length + + var c = this.cursor; + var lb = this.limit_backward; + + var common_i = 0; + var common_j = 0; + + var first_key_inspected = false; + + while (true) + { + var k = i + ((j - i) >> 1); + var diff = 0; + var common = common_i < common_j ? common_i : common_j; + var w = v[k]; + var i2; + for (i2 = w[0].length - 1 - common; i2 >= 0; i2--) + { + if (c - common == lb) + { + diff = -1; + break; + } + diff = this.current.charCodeAt(c - 1 - common) - w[0].charCodeAt(i2); + if (diff != 0) break; + common++; + } + if (diff < 0) + { + j = k; + common_j = common; + } + else + { + i = k; + common_i = common; + } + if (j - i <= 1) + { + if (i > 0) break; + if (j == i) break; + if (first_key_inspected) break; + first_key_inspected = true; + } + } + do { + var w = v[i]; + if (common_i >= w[0].length) + { + this.cursor = c - w[0].length; + if (w.length < 4) return w[2]; + var res = w[3](this); + this.cursor = c - w[0].length; + if (res) return w[2]; + } + i = w[1]; + } while (i >= 0); + return 0; + }; + + /* to replace chars between c_bra and c_ket in this.current by the + * chars in s. + */ + /** + * @param {number} c_bra + * @param {number} c_ket + * @param {string} s + * @return {number} + */ + this.replace_s = function(c_bra, c_ket, s) + { + /** @protected */ + var adjustment = s.length - (c_ket - c_bra); + this.current = this.current.slice(0, c_bra) + s + this.current.slice(c_ket); + this.limit += adjustment; + if (this.cursor >= c_ket) this.cursor += adjustment; + else if (this.cursor > c_bra) this.cursor = c_bra; + return adjustment; + }; + + /** + * @return {boolean} + */ + this.slice_check = function() + { + /** @protected */ + if (this.bra < 0 || + this.bra > this.ket || + this.ket > this.limit || + this.limit > this.current.length) + { + return false; + } + return true; + }; + + /** + * @param {number} c_bra + * @return {boolean} + */ + this.slice_from = function(s) + { + /** @protected */ + var result = false; + if (this.slice_check()) + { + this.replace_s(this.bra, this.ket, s); + result = true; + } + return result; + }; + + /** + * @return {boolean} + */ + this.slice_del = function() + { + /** @protected */ + return this.slice_from(""); + }; + + /** + * @param {number} c_bra + * @param {number} c_ket + * @param {string} s + */ + this.insert = function(c_bra, c_ket, s) + { + /** @protected */ + var adjustment = this.replace_s(c_bra, c_ket, s); + if (c_bra <= this.bra) this.bra += adjustment; + if (c_bra <= this.ket) this.ket += adjustment; + }; + + /** + * @return {string} + */ + this.slice_to = function() + { + /** @protected */ + var result = ''; + if (this.slice_check()) + { + result = this.current.slice(this.bra, this.ket); + } + return result; + }; + + /** + * @return {string} + */ + this.assign_to = function() + { + /** @protected */ + return this.current.slice(0, this.limit); + }; +}; diff --git a/src/scitex/_sphinx_html/_static/basic.css b/src/scitex/_sphinx_html/_static/basic.css new file mode 100644 index 000000000..4738b2edc --- /dev/null +++ b/src/scitex/_sphinx_html/_static/basic.css @@ -0,0 +1,906 @@ +/* + * Sphinx stylesheet -- basic theme. + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin-top: 10px; +} + +ul.search li { + padding: 5px 0; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_static/check-solid.svg b/src/scitex/_sphinx_html/_static/check-solid.svg new file mode 100644 index 000000000..92fad4b5c --- /dev/null +++ b/src/scitex/_sphinx_html/_static/check-solid.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/scitex/_sphinx_html/_static/clipboard.min.js b/src/scitex/_sphinx_html/_static/clipboard.min.js new file mode 100644 index 000000000..54b3c4638 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/clipboard.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.8 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return o}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),c=n.n(e);function a(t){try{return document.execCommand(t)}catch(t){return}}var f=function(t){t=c()(t);return a("cut"),t};var l=function(t){var e,n,o,r=1 + + + + diff --git a/src/scitex/_sphinx_html/_static/copybutton.css b/src/scitex/_sphinx_html/_static/copybutton.css new file mode 100644 index 000000000..f1916ec7d --- /dev/null +++ b/src/scitex/_sphinx_html/_static/copybutton.css @@ -0,0 +1,94 @@ +/* Copy buttons */ +button.copybtn { + position: absolute; + display: flex; + top: .3em; + right: .3em; + width: 1.7em; + height: 1.7em; + opacity: 0; + transition: opacity 0.3s, border .3s, background-color .3s; + user-select: none; + padding: 0; + border: none; + outline: none; + border-radius: 0.4em; + /* The colors that GitHub uses */ + border: #1b1f2426 1px solid; + background-color: #f6f8fa; + color: #57606a; +} + +button.copybtn.success { + border-color: #22863a; + color: #22863a; +} + +button.copybtn svg { + stroke: currentColor; + width: 1.5em; + height: 1.5em; + padding: 0.1em; +} + +div.highlight { + position: relative; +} + +/* Show the copybutton */ +.highlight:hover button.copybtn, button.copybtn.success { + opacity: 1; +} + +.highlight button.copybtn:hover { + background-color: rgb(235, 235, 235); +} + +.highlight button.copybtn:active { + background-color: rgb(187, 187, 187); +} + +/** + * A minimal CSS-only tooltip copied from: + * https://codepen.io/mildrenben/pen/rVBrpK + * + * To use, write HTML like the following: + * + *

Short

+ */ + .o-tooltip--left { + position: relative; + } + + .o-tooltip--left:after { + opacity: 0; + visibility: hidden; + position: absolute; + content: attr(data-tooltip); + padding: .2em; + font-size: .8em; + left: -.2em; + background: grey; + color: white; + white-space: nowrap; + z-index: 2; + border-radius: 2px; + transform: translateX(-102%) translateY(0); + transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); +} + +.o-tooltip--left:hover:after { + display: block; + opacity: 1; + visibility: visible; + transform: translateX(-100%) translateY(0); + transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); + transition-delay: .5s; +} + +/* By default the copy button shouldn't show up when printing a page */ +@media print { + button.copybtn { + display: none; + } +} diff --git a/src/scitex/_sphinx_html/_static/copybutton.js b/src/scitex/_sphinx_html/_static/copybutton.js new file mode 100644 index 000000000..2ea7ff3e2 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/copybutton.js @@ -0,0 +1,248 @@ +// Localization support +const messages = { + 'en': { + 'copy': 'Copy', + 'copy_to_clipboard': 'Copy to clipboard', + 'copy_success': 'Copied!', + 'copy_failure': 'Failed to copy', + }, + 'es' : { + 'copy': 'Copiar', + 'copy_to_clipboard': 'Copiar al portapapeles', + 'copy_success': '¡Copiado!', + 'copy_failure': 'Error al copiar', + }, + 'de' : { + 'copy': 'Kopieren', + 'copy_to_clipboard': 'In die Zwischenablage kopieren', + 'copy_success': 'Kopiert!', + 'copy_failure': 'Fehler beim Kopieren', + }, + 'fr' : { + 'copy': 'Copier', + 'copy_to_clipboard': 'Copier dans le presse-papier', + 'copy_success': 'Copié !', + 'copy_failure': 'Échec de la copie', + }, + 'ru': { + 'copy': 'Скопировать', + 'copy_to_clipboard': 'Скопировать в буфер', + 'copy_success': 'Скопировано!', + 'copy_failure': 'Не удалось скопировать', + }, + 'zh-CN': { + 'copy': '复制', + 'copy_to_clipboard': '复制到剪贴板', + 'copy_success': '复制成功!', + 'copy_failure': '复制失败', + }, + 'it' : { + 'copy': 'Copiare', + 'copy_to_clipboard': 'Copiato negli appunti', + 'copy_success': 'Copiato!', + 'copy_failure': 'Errore durante la copia', + } +} + +let locale = 'en' +if( document.documentElement.lang !== undefined + && messages[document.documentElement.lang] !== undefined ) { + locale = document.documentElement.lang +} + +let doc_url_root = DOCUMENTATION_OPTIONS.URL_ROOT; +if (doc_url_root == '#') { + doc_url_root = ''; +} + +/** + * SVG files for our copy buttons + */ +let iconCheck = ` + ${messages[locale]['copy_success']} + + +` + +// If the user specified their own SVG use that, otherwise use the default +let iconCopy = ``; +if (!iconCopy) { + iconCopy = ` + ${messages[locale]['copy_to_clipboard']} + + + +` +} + +/** + * Set up copy/paste for code blocks + */ + +const runWhenDOMLoaded = cb => { + if (document.readyState != 'loading') { + cb() + } else if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', cb) + } else { + document.attachEvent('onreadystatechange', function() { + if (document.readyState == 'complete') cb() + }) + } +} + +const codeCellId = index => `codecell${index}` + +// Clears selected text since ClipboardJS will select the text when copying +const clearSelection = () => { + if (window.getSelection) { + window.getSelection().removeAllRanges() + } else if (document.selection) { + document.selection.empty() + } +} + +// Changes tooltip text for a moment, then changes it back +// We want the timeout of our `success` class to be a bit shorter than the +// tooltip and icon change, so that we can hide the icon before changing back. +var timeoutIcon = 2000; +var timeoutSuccessClass = 1500; + +const temporarilyChangeTooltip = (el, oldText, newText) => { + el.setAttribute('data-tooltip', newText) + el.classList.add('success') + // Remove success a little bit sooner than we change the tooltip + // So that we can use CSS to hide the copybutton first + setTimeout(() => el.classList.remove('success'), timeoutSuccessClass) + setTimeout(() => el.setAttribute('data-tooltip', oldText), timeoutIcon) +} + +// Changes the copy button icon for two seconds, then changes it back +const temporarilyChangeIcon = (el) => { + el.innerHTML = iconCheck; + setTimeout(() => {el.innerHTML = iconCopy}, timeoutIcon) +} + +const addCopyButtonToCodeCells = () => { + // If ClipboardJS hasn't loaded, wait a bit and try again. This + // happens because we load ClipboardJS asynchronously. + if (window.ClipboardJS === undefined) { + setTimeout(addCopyButtonToCodeCells, 250) + return + } + + // Add copybuttons to all of our code cells + const COPYBUTTON_SELECTOR = 'div.highlight pre'; + const codeCells = document.querySelectorAll(COPYBUTTON_SELECTOR) + codeCells.forEach((codeCell, index) => { + const id = codeCellId(index) + codeCell.setAttribute('id', id) + + const clipboardButton = id => + `` + codeCell.insertAdjacentHTML('afterend', clipboardButton(id)) + }) + +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +/** + * Removes excluded text from a Node. + * + * @param {Node} target Node to filter. + * @param {string} exclude CSS selector of nodes to exclude. + * @returns {DOMString} Text from `target` with text removed. + */ +function filterText(target, exclude) { + const clone = target.cloneNode(true); // clone as to not modify the live DOM + if (exclude) { + // remove excluded nodes + clone.querySelectorAll(exclude).forEach(node => node.remove()); + } + return clone.innerText; +} + +// Callback when a copy button is clicked. Will be passed the node that was clicked +// should then grab the text and replace pieces of text that shouldn't be used in output +function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { + var regexp; + var match; + + // Do we check for line continuation characters and "HERE-documents"? + var useLineCont = !!lineContinuationChar + var useHereDoc = !!hereDocDelim + + // create regexp to capture prompt and remaining line + if (isRegexp) { + regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') + } else { + regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') + } + + const outputLines = []; + var promptFound = false; + var gotLineCont = false; + var gotHereDoc = false; + const lineGotPrompt = []; + for (const line of textContent.split('\n')) { + match = line.match(regexp) + if (match || gotLineCont || gotHereDoc) { + promptFound = regexp.test(line) + lineGotPrompt.push(promptFound) + if (removePrompts && promptFound) { + outputLines.push(match[2]) + } else { + outputLines.push(line) + } + gotLineCont = line.endsWith(lineContinuationChar) & useLineCont + if (line.includes(hereDocDelim) & useHereDoc) + gotHereDoc = !gotHereDoc + } else if (!onlyCopyPromptLines) { + outputLines.push(line) + } else if (copyEmptyLines && line.trim() === '') { + outputLines.push(line) + } + } + + // If no lines with the prompt were found then just use original lines + if (lineGotPrompt.some(v => v === true)) { + textContent = outputLines.join('\n'); + } + + // Remove a trailing newline to avoid auto-running when pasting + if (textContent.endsWith("\n")) { + textContent = textContent.slice(0, -1) + } + return textContent +} + + +var copyTargetText = (trigger) => { + var target = document.querySelector(trigger.attributes['data-clipboard-target'].value); + + // get filtered text + let exclude = '.linenos'; + + let text = filterText(target, exclude); + return formatCopyText(text, '', false, true, true, true, '', '') +} + + // Initialize with a callback so we can modify the text before copy + const clipboard = new ClipboardJS('.copybtn', {text: copyTargetText}) + + // Update UI with error/success messages + clipboard.on('success', event => { + clearSelection() + temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_success']) + temporarilyChangeIcon(event.trigger) + }) + + clipboard.on('error', event => { + temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_failure']) + }) +} + +runWhenDOMLoaded(addCopyButtonToCodeCells) \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_static/copybutton_funcs.js b/src/scitex/_sphinx_html/_static/copybutton_funcs.js new file mode 100644 index 000000000..dbe1aaad7 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/copybutton_funcs.js @@ -0,0 +1,73 @@ +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +/** + * Removes excluded text from a Node. + * + * @param {Node} target Node to filter. + * @param {string} exclude CSS selector of nodes to exclude. + * @returns {DOMString} Text from `target` with text removed. + */ +export function filterText(target, exclude) { + const clone = target.cloneNode(true); // clone as to not modify the live DOM + if (exclude) { + // remove excluded nodes + clone.querySelectorAll(exclude).forEach(node => node.remove()); + } + return clone.innerText; +} + +// Callback when a copy button is clicked. Will be passed the node that was clicked +// should then grab the text and replace pieces of text that shouldn't be used in output +export function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { + var regexp; + var match; + + // Do we check for line continuation characters and "HERE-documents"? + var useLineCont = !!lineContinuationChar + var useHereDoc = !!hereDocDelim + + // create regexp to capture prompt and remaining line + if (isRegexp) { + regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') + } else { + regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') + } + + const outputLines = []; + var promptFound = false; + var gotLineCont = false; + var gotHereDoc = false; + const lineGotPrompt = []; + for (const line of textContent.split('\n')) { + match = line.match(regexp) + if (match || gotLineCont || gotHereDoc) { + promptFound = regexp.test(line) + lineGotPrompt.push(promptFound) + if (removePrompts && promptFound) { + outputLines.push(match[2]) + } else { + outputLines.push(line) + } + gotLineCont = line.endsWith(lineContinuationChar) & useLineCont + if (line.includes(hereDocDelim) & useHereDoc) + gotHereDoc = !gotHereDoc + } else if (!onlyCopyPromptLines) { + outputLines.push(line) + } else if (copyEmptyLines && line.trim() === '') { + outputLines.push(line) + } + } + + // If no lines with the prompt were found then just use original lines + if (lineGotPrompt.some(v => v === true)) { + textContent = outputLines.join('\n'); + } + + // Remove a trailing newline to avoid auto-running when pasting + if (textContent.endsWith("\n")) { + textContent = textContent.slice(0, -1) + } + return textContent +} diff --git a/src/scitex/_sphinx_html/_static/css/badge_only.css b/src/scitex/_sphinx_html/_static/css/badge_only.css new file mode 100644 index 000000000..88ba55b96 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/css/badge_only.css @@ -0,0 +1 @@ +.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions .rst-other-versions .rtd-current-item{font-weight:700}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}#flyout-search-form{padding:6px} \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_static/css/fonts/Roboto-Slab-Regular.woff b/src/scitex/_sphinx_html/_static/css/fonts/Roboto-Slab-Regular.woff new file mode 100644 index 000000000..f815f63f9 Binary files /dev/null and b/src/scitex/_sphinx_html/_static/css/fonts/Roboto-Slab-Regular.woff differ diff --git a/src/scitex/_sphinx_html/_static/css/fonts/Roboto-Slab-Regular.woff2 b/src/scitex/_sphinx_html/_static/css/fonts/Roboto-Slab-Regular.woff2 new file mode 100644 index 000000000..f2c76e5bd Binary files /dev/null and b/src/scitex/_sphinx_html/_static/css/fonts/Roboto-Slab-Regular.woff2 differ diff --git a/src/scitex/_sphinx_html/_static/css/fonts/fontawesome-webfont.eot b/src/scitex/_sphinx_html/_static/css/fonts/fontawesome-webfont.eot new file mode 100644 index 000000000..e9f60ca95 Binary files /dev/null and b/src/scitex/_sphinx_html/_static/css/fonts/fontawesome-webfont.eot differ diff --git a/src/scitex/_sphinx_html/_static/css/fonts/fontawesome-webfont.svg b/src/scitex/_sphinx_html/_static/css/fonts/fontawesome-webfont.svg new file mode 100644 index 000000000..855c845e5 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/css/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/scitex/_sphinx_html/_static/css/fonts/fontawesome-webfont.ttf b/src/scitex/_sphinx_html/_static/css/fonts/fontawesome-webfont.ttf new file mode 100644 index 000000000..35acda2fa Binary files /dev/null and b/src/scitex/_sphinx_html/_static/css/fonts/fontawesome-webfont.ttf differ diff --git a/src/scitex/_sphinx_html/_static/css/fonts/fontawesome-webfont.woff b/src/scitex/_sphinx_html/_static/css/fonts/fontawesome-webfont.woff new file mode 100644 index 000000000..400014a4b Binary files /dev/null and b/src/scitex/_sphinx_html/_static/css/fonts/fontawesome-webfont.woff differ diff --git a/src/scitex/_sphinx_html/_static/css/fonts/fontawesome-webfont.woff2 b/src/scitex/_sphinx_html/_static/css/fonts/fontawesome-webfont.woff2 new file mode 100644 index 000000000..4d13fc604 Binary files /dev/null and b/src/scitex/_sphinx_html/_static/css/fonts/fontawesome-webfont.woff2 differ diff --git a/src/scitex/_sphinx_html/_static/css/fonts/lato-normal-italic.woff b/src/scitex/_sphinx_html/_static/css/fonts/lato-normal-italic.woff new file mode 100644 index 000000000..76114bc03 Binary files /dev/null and b/src/scitex/_sphinx_html/_static/css/fonts/lato-normal-italic.woff differ diff --git a/src/scitex/_sphinx_html/_static/css/fonts/lato-normal-italic.woff2 b/src/scitex/_sphinx_html/_static/css/fonts/lato-normal-italic.woff2 new file mode 100644 index 000000000..3404f37e2 Binary files /dev/null and b/src/scitex/_sphinx_html/_static/css/fonts/lato-normal-italic.woff2 differ diff --git a/src/scitex/_sphinx_html/_static/css/fonts/lato-normal.woff b/src/scitex/_sphinx_html/_static/css/fonts/lato-normal.woff new file mode 100644 index 000000000..ae1307ff5 Binary files /dev/null and b/src/scitex/_sphinx_html/_static/css/fonts/lato-normal.woff differ diff --git a/src/scitex/_sphinx_html/_static/css/fonts/lato-normal.woff2 b/src/scitex/_sphinx_html/_static/css/fonts/lato-normal.woff2 new file mode 100644 index 000000000..3bf984332 Binary files /dev/null and b/src/scitex/_sphinx_html/_static/css/fonts/lato-normal.woff2 differ diff --git a/src/scitex/_sphinx_html/_static/css/theme.css b/src/scitex/_sphinx_html/_static/css/theme.css new file mode 100644 index 000000000..a88467c1b --- /dev/null +++ b/src/scitex/_sphinx_html/_static/css/theme.css @@ -0,0 +1,4 @@ +html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden],audio:not([controls]){display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;text-decoration:none}ins,mark{color:#000}mark{background:#ff0;font-style:italic;font-weight:700}.rst-content code,.rst-content tt,code,kbd,pre,samp{font-family:monospace,serif;_font-family:courier new,monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:after,q:before{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}dl,ol,ul{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure,form{margin:0}label{cursor:pointer}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}textarea{resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none!important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{body,html,section{background:none!important}*{box-shadow:none!important;text-shadow:none!important;filter:none!important;-ms-filter:none!important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}.rst-content .toctree-wrapper>p.caption,h2,h3,p{orphans:3;widows:3}.rst-content .toctree-wrapper>p.caption,h2,h3{page-break-after:avoid}}.btn,.fa:before,.icon:before,.rst-content .admonition,.rst-content .admonition-title:before,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .code-block-caption .headerlink:before,.rst-content .danger,.rst-content .eqno .headerlink:before,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-alert,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before,input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:FontAwesome;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713);src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix&v=4.7.0) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#fontawesomeregular) format("svg");font-weight:400;font-style:normal}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa-pull-left.icon,.fa.fa-pull-left,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content .eqno .fa-pull-left.headerlink,.rst-content .fa-pull-left.admonition-title,.rst-content code.download span.fa-pull-left:first-child,.rst-content dl dt .fa-pull-left.headerlink,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content p .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.wy-menu-vertical li.current>a button.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-left.toctree-expand,.wy-menu-vertical li button.fa-pull-left.toctree-expand{margin-right:.3em}.fa-pull-right.icon,.fa.fa-pull-right,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content .eqno .fa-pull-right.headerlink,.rst-content .fa-pull-right.admonition-title,.rst-content code.download span.fa-pull-right:first-child,.rst-content dl dt .fa-pull-right.headerlink,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content p .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.wy-menu-vertical li.current>a button.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-right.toctree-expand,.wy-menu-vertical li button.fa-pull-right.toctree-expand{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pull-left.icon,.rst-content .code-block-caption .pull-left.headerlink,.rst-content .eqno .pull-left.headerlink,.rst-content .pull-left.admonition-title,.rst-content code.download span.pull-left:first-child,.rst-content dl dt .pull-left.headerlink,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content p .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.wy-menu-vertical li.current>a button.pull-left.toctree-expand,.wy-menu-vertical li.on a button.pull-left.toctree-expand,.wy-menu-vertical li button.pull-left.toctree-expand{margin-right:.3em}.fa.pull-right,.pull-right.icon,.rst-content .code-block-caption .pull-right.headerlink,.rst-content .eqno .pull-right.headerlink,.rst-content .pull-right.admonition-title,.rst-content code.download span.pull-right:first-child,.rst-content dl dt .pull-right.headerlink,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content p .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.wy-menu-vertical li.current>a button.pull-right.toctree-expand,.wy-menu-vertical li.on a button.pull-right.toctree-expand,.wy-menu-vertical li button.pull-right.toctree-expand{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-close:before,.fa-remove:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-cog:before,.fa-gear:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-repeat:before,.fa-rotate-right:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.rst-content .admonition-title:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-exclamation-triangle:before,.fa-warning:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-cogs:before,.fa-gears:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-floppy-o:before,.fa-save:before{content:""}.fa-square:before{content:""}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.icon-caret-down:before,.wy-dropdown .caret:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-sort:before,.fa-unsorted:before{content:""}.fa-sort-desc:before,.fa-sort-down:before{content:""}.fa-sort-asc:before,.fa-sort-up:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-gavel:before,.fa-legal:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-bolt:before,.fa-flash:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-clipboard:before,.fa-paste:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-chain-broken:before,.fa-unlink:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:""}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:""}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:""}.fa-eur:before,.fa-euro:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-inr:before,.fa-rupee:before{content:""}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:""}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:""}.fa-krw:before,.fa-won:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-try:before,.fa-turkish-lira:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li button.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-bank:before,.fa-institution:before,.fa-university:before{content:""}.fa-graduation-cap:before,.fa-mortar-board:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:""}.fa-file-archive-o:before,.fa-file-zip-o:before{content:""}.fa-file-audio-o:before,.fa-file-sound-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:""}.fa-empire:before,.fa-ge:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-paper-plane:before,.fa-send:before{content:""}.fa-paper-plane-o:before,.fa-send-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-bed:before,.fa-hotel:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-y-combinator:before,.fa-yc:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-television:before,.fa-tv:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:""}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-sign-language:before,.fa-signing:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-address-card:before,.fa-vcard:before{content:""}.fa-address-card-o:before,.fa-vcard-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{font-family:inherit}.fa:before,.icon:before,.rst-content .admonition-title:before,.rst-content .code-block-caption .headerlink:before,.rst-content .eqno .headerlink:before,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before{font-family:FontAwesome;display:inline-block;font-style:normal;font-weight:400;line-height:1;text-decoration:inherit}.rst-content .code-block-caption a .headerlink,.rst-content .eqno a .headerlink,.rst-content a .admonition-title,.rst-content code.download a span:first-child,.rst-content dl dt a .headerlink,.rst-content h1 a .headerlink,.rst-content h2 a .headerlink,.rst-content h3 a .headerlink,.rst-content h4 a .headerlink,.rst-content h5 a .headerlink,.rst-content h6 a .headerlink,.rst-content p.caption a .headerlink,.rst-content p a .headerlink,.rst-content table>caption a .headerlink,.rst-content tt.download a span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li a button.toctree-expand,a .fa,a .icon,a .rst-content .admonition-title,a .rst-content .code-block-caption .headerlink,a .rst-content .eqno .headerlink,a .rst-content code.download span:first-child,a .rst-content dl dt .headerlink,a .rst-content h1 .headerlink,a .rst-content h2 .headerlink,a .rst-content h3 .headerlink,a .rst-content h4 .headerlink,a .rst-content h5 .headerlink,a .rst-content h6 .headerlink,a .rst-content p.caption .headerlink,a .rst-content p .headerlink,a .rst-content table>caption .headerlink,a .rst-content tt.download span:first-child,a .wy-menu-vertical li button.toctree-expand{display:inline-block;text-decoration:inherit}.btn .fa,.btn .icon,.btn .rst-content .admonition-title,.btn .rst-content .code-block-caption .headerlink,.btn .rst-content .eqno .headerlink,.btn .rst-content code.download span:first-child,.btn .rst-content dl dt .headerlink,.btn .rst-content h1 .headerlink,.btn .rst-content h2 .headerlink,.btn .rst-content h3 .headerlink,.btn .rst-content h4 .headerlink,.btn .rst-content h5 .headerlink,.btn .rst-content h6 .headerlink,.btn .rst-content p .headerlink,.btn .rst-content table>caption .headerlink,.btn .rst-content tt.download span:first-child,.btn .wy-menu-vertical li.current>a button.toctree-expand,.btn .wy-menu-vertical li.on a button.toctree-expand,.btn .wy-menu-vertical li button.toctree-expand,.nav .fa,.nav .icon,.nav .rst-content .admonition-title,.nav .rst-content .code-block-caption .headerlink,.nav .rst-content .eqno .headerlink,.nav .rst-content code.download span:first-child,.nav .rst-content dl dt .headerlink,.nav .rst-content h1 .headerlink,.nav .rst-content h2 .headerlink,.nav .rst-content h3 .headerlink,.nav .rst-content h4 .headerlink,.nav .rst-content h5 .headerlink,.nav .rst-content h6 .headerlink,.nav .rst-content p .headerlink,.nav .rst-content table>caption .headerlink,.nav .rst-content tt.download span:first-child,.nav .wy-menu-vertical li.current>a button.toctree-expand,.nav .wy-menu-vertical li.on a button.toctree-expand,.nav .wy-menu-vertical li button.toctree-expand,.rst-content .btn .admonition-title,.rst-content .code-block-caption .btn .headerlink,.rst-content .code-block-caption .nav .headerlink,.rst-content .eqno .btn .headerlink,.rst-content .eqno .nav .headerlink,.rst-content .nav .admonition-title,.rst-content code.download .btn span:first-child,.rst-content code.download .nav span:first-child,.rst-content dl dt .btn .headerlink,.rst-content dl dt .nav .headerlink,.rst-content h1 .btn .headerlink,.rst-content h1 .nav .headerlink,.rst-content h2 .btn .headerlink,.rst-content h2 .nav .headerlink,.rst-content h3 .btn .headerlink,.rst-content h3 .nav .headerlink,.rst-content h4 .btn .headerlink,.rst-content h4 .nav .headerlink,.rst-content h5 .btn .headerlink,.rst-content h5 .nav .headerlink,.rst-content h6 .btn .headerlink,.rst-content h6 .nav .headerlink,.rst-content p .btn .headerlink,.rst-content p .nav .headerlink,.rst-content table>caption .btn .headerlink,.rst-content table>caption .nav .headerlink,.rst-content tt.download .btn span:first-child,.rst-content tt.download .nav span:first-child,.wy-menu-vertical li .btn button.toctree-expand,.wy-menu-vertical li.current>a .btn button.toctree-expand,.wy-menu-vertical li.current>a .nav button.toctree-expand,.wy-menu-vertical li .nav button.toctree-expand,.wy-menu-vertical li.on a .btn button.toctree-expand,.wy-menu-vertical li.on a .nav button.toctree-expand{display:inline}.btn .fa-large.icon,.btn .fa.fa-large,.btn .rst-content .code-block-caption .fa-large.headerlink,.btn .rst-content .eqno .fa-large.headerlink,.btn .rst-content .fa-large.admonition-title,.btn .rst-content code.download span.fa-large:first-child,.btn .rst-content dl dt .fa-large.headerlink,.btn .rst-content h1 .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.btn .rst-content p .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.btn .wy-menu-vertical li button.fa-large.toctree-expand,.nav .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .code-block-caption .fa-large.headerlink,.nav .rst-content .eqno .fa-large.headerlink,.nav .rst-content .fa-large.admonition-title,.nav .rst-content code.download span.fa-large:first-child,.nav .rst-content dl dt .fa-large.headerlink,.nav .rst-content h1 .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.nav .rst-content p .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.nav .wy-menu-vertical li button.fa-large.toctree-expand,.rst-content .btn .fa-large.admonition-title,.rst-content .code-block-caption .btn .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.rst-content .eqno .btn .fa-large.headerlink,.rst-content .eqno .nav .fa-large.headerlink,.rst-content .nav .fa-large.admonition-title,.rst-content code.download .btn span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.rst-content dl dt .btn .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.rst-content p .btn .fa-large.headerlink,.rst-content p .nav .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.rst-content tt.download .btn span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.wy-menu-vertical li .btn button.fa-large.toctree-expand,.wy-menu-vertical li .nav button.fa-large.toctree-expand{line-height:.9em}.btn .fa-spin.icon,.btn .fa.fa-spin,.btn .rst-content .code-block-caption .fa-spin.headerlink,.btn .rst-content .eqno .fa-spin.headerlink,.btn .rst-content .fa-spin.admonition-title,.btn .rst-content code.download span.fa-spin:first-child,.btn .rst-content dl dt .fa-spin.headerlink,.btn .rst-content h1 .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.btn .rst-content p .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.btn .wy-menu-vertical li button.fa-spin.toctree-expand,.nav .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .code-block-caption .fa-spin.headerlink,.nav .rst-content .eqno .fa-spin.headerlink,.nav .rst-content .fa-spin.admonition-title,.nav .rst-content code.download span.fa-spin:first-child,.nav .rst-content dl dt .fa-spin.headerlink,.nav .rst-content h1 .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.nav .rst-content p .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.nav .wy-menu-vertical li button.fa-spin.toctree-expand,.rst-content .btn .fa-spin.admonition-title,.rst-content .code-block-caption .btn .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.rst-content .eqno .btn .fa-spin.headerlink,.rst-content .eqno .nav .fa-spin.headerlink,.rst-content .nav .fa-spin.admonition-title,.rst-content code.download .btn span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.rst-content dl dt .btn .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.rst-content p .btn .fa-spin.headerlink,.rst-content p .nav .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.rst-content tt.download .btn span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.wy-menu-vertical li .btn button.fa-spin.toctree-expand,.wy-menu-vertical li .nav button.fa-spin.toctree-expand{display:inline-block}.btn.fa:before,.btn.icon:before,.rst-content .btn.admonition-title:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content .eqno .btn.headerlink:before,.rst-content code.download span.btn:first-child:before,.rst-content dl dt .btn.headerlink:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content p .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.wy-menu-vertical li button.btn.toctree-expand:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.btn.icon:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content .eqno .btn.headerlink:hover:before,.rst-content code.download span.btn:first-child:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content p .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.wy-menu-vertical li button.btn.toctree-expand:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .icon:before,.btn-mini .rst-content .admonition-title:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.btn-mini .rst-content .eqno .headerlink:before,.btn-mini .rst-content code.download span:first-child:before,.btn-mini .rst-content dl dt .headerlink:before,.btn-mini .rst-content h1 .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.btn-mini .rst-content p .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.btn-mini .wy-menu-vertical li button.toctree-expand:before,.rst-content .btn-mini .admonition-title:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.rst-content .eqno .btn-mini .headerlink:before,.rst-content code.download .btn-mini span:first-child:before,.rst-content dl dt .btn-mini .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.rst-content p .btn-mini .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.rst-content tt.download .btn-mini span:first-child:before,.wy-menu-vertical li .btn-mini button.toctree-expand:before{font-size:14px;vertical-align:-15%}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.wy-alert{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.rst-content .admonition-title,.wy-alert-title{font-weight:700;display:block;color:#fff;background:#6ab0de;padding:6px 12px;margin:-12px -12px 12px}.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.admonition,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.wy-alert.wy-alert-danger{background:#fdf3f2}.rst-content .danger .admonition-title,.rst-content .danger .wy-alert-title,.rst-content .error .admonition-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .admonition-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.wy-alert.wy-alert-danger .wy-alert-title{background:#f29f97}.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .warning,.rst-content .wy-alert-warning.admonition,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.note,.rst-content .wy-alert-warning.seealso,.rst-content .wy-alert-warning.tip,.wy-alert.wy-alert-warning{background:#ffedcc}.rst-content .admonition-todo .admonition-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .attention .admonition-title,.rst-content .attention .wy-alert-title,.rst-content .caution .admonition-title,.rst-content .caution .wy-alert-title,.rst-content .warning .admonition-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.admonition .admonition-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.wy-alert.wy-alert-warning .wy-alert-title{background:#f0b37e}.rst-content .note,.rst-content .seealso,.rst-content .wy-alert-info.admonition,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.wy-alert.wy-alert-info{background:#e7f2fa}.rst-content .note .admonition-title,.rst-content .note .wy-alert-title,.rst-content .seealso .admonition-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .admonition-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.wy-alert.wy-alert-info .wy-alert-title{background:#6ab0de}.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.admonition,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.warning,.wy-alert.wy-alert-success{background:#dbfaf4}.rst-content .hint .admonition-title,.rst-content .hint .wy-alert-title,.rst-content .important .admonition-title,.rst-content .important .wy-alert-title,.rst-content .tip .admonition-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .admonition-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.wy-alert.wy-alert-success .wy-alert-title{background:#1abc9c}.rst-content .wy-alert-neutral.admonition,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.wy-alert.wy-alert-neutral{background:#f3f6f6}.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .admonition-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.wy-alert.wy-alert-neutral .wy-alert-title{color:#404040;background:#e1e4e5}.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.wy-alert.wy-alert-neutral a{color:#2980b9}.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .note p:last-child,.rst-content .seealso p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.wy-alert p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width:768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px;color:#fff;border:1px solid rgba(0,0,0,.1);background-color:#27ae60;text-decoration:none;font-weight:400;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 rgba(0,0,0,.1);outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:inset 0 -1px 0 0 rgba(0,0,0,.05),inset 0 2px 0 0 rgba(0,0,0,.1);padding:8px 12px 6px}.btn:visited{color:#fff}.btn-disabled,.btn-disabled:active,.btn-disabled:focus,.btn-disabled:hover,.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9!important}.btn-info:hover{background-color:#2e8ece!important}.btn-neutral{background-color:#f3f6f6!important;color:#404040!important}.btn-neutral:hover{background-color:#e5ebeb!important;color:#404040}.btn-neutral:visited{color:#404040!important}.btn-success{background-color:#27ae60!important}.btn-success:hover{background-color:#295!important}.btn-danger{background-color:#e74c3c!important}.btn-danger:hover{background-color:#ea6153!important}.btn-warning{background-color:#e67e22!important}.btn-warning:hover{background-color:#e98b39!important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f!important}.btn-link{background-color:transparent!important;color:#2980b9;box-shadow:none;border-color:transparent!important}.btn-link:active,.btn-link:hover{background-color:transparent!important;color:#409ad5!important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:after,.wy-btn-group:before{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:1px solid #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:1px solid #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type=search]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned .wy-help-inline,.wy-form-aligned input,.wy-form-aligned label,.wy-form-aligned select,.wy-form-aligned textarea{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{margin:0}fieldset,legend{border:0;padding:0}legend{width:100%;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label,legend{display:block}label{margin:0 0 .3125em;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;max-width:1200px;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:after,.wy-control-group:before{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full input[type=color],.wy-control-group .wy-form-full input[type=date],.wy-control-group .wy-form-full input[type=datetime-local],.wy-control-group .wy-form-full input[type=datetime],.wy-control-group .wy-form-full input[type=email],.wy-control-group .wy-form-full input[type=month],.wy-control-group .wy-form-full input[type=number],.wy-control-group .wy-form-full input[type=password],.wy-control-group .wy-form-full input[type=search],.wy-control-group .wy-form-full input[type=tel],.wy-control-group .wy-form-full input[type=text],.wy-control-group .wy-form-full input[type=time],.wy-control-group .wy-form-full input[type=url],.wy-control-group .wy-form-full input[type=week],.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves input[type=color],.wy-control-group .wy-form-halves input[type=date],.wy-control-group .wy-form-halves input[type=datetime-local],.wy-control-group .wy-form-halves input[type=datetime],.wy-control-group .wy-form-halves input[type=email],.wy-control-group .wy-form-halves input[type=month],.wy-control-group .wy-form-halves input[type=number],.wy-control-group .wy-form-halves input[type=password],.wy-control-group .wy-form-halves input[type=search],.wy-control-group .wy-form-halves input[type=tel],.wy-control-group .wy-form-halves input[type=text],.wy-control-group .wy-form-halves input[type=time],.wy-control-group .wy-form-halves input[type=url],.wy-control-group .wy-form-halves input[type=week],.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds input[type=color],.wy-control-group .wy-form-thirds input[type=date],.wy-control-group .wy-form-thirds input[type=datetime-local],.wy-control-group .wy-form-thirds input[type=datetime],.wy-control-group .wy-form-thirds input[type=email],.wy-control-group .wy-form-thirds input[type=month],.wy-control-group .wy-form-thirds input[type=number],.wy-control-group .wy-form-thirds input[type=password],.wy-control-group .wy-form-thirds input[type=search],.wy-control-group .wy-form-thirds input[type=tel],.wy-control-group .wy-form-thirds input[type=text],.wy-control-group .wy-form-thirds input[type=time],.wy-control-group .wy-form-thirds input[type=url],.wy-control-group .wy-form-thirds input[type=week],.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full{float:left;display:block;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child,.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(odd){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child,.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control,.wy-control-no-input{margin:6px 0 0;font-size:90%}.wy-control-no-input{display:inline-block}.wy-control-group.fluid-input input[type=color],.wy-control-group.fluid-input input[type=date],.wy-control-group.fluid-input input[type=datetime-local],.wy-control-group.fluid-input input[type=datetime],.wy-control-group.fluid-input input[type=email],.wy-control-group.fluid-input input[type=month],.wy-control-group.fluid-input input[type=number],.wy-control-group.fluid-input input[type=password],.wy-control-group.fluid-input input[type=search],.wy-control-group.fluid-input input[type=tel],.wy-control-group.fluid-input input[type=text],.wy-control-group.fluid-input input[type=time],.wy-control-group.fluid-input input[type=url],.wy-control-group.fluid-input input[type=week]{width:100%}.wy-form-message-inline{padding-left:.3em;color:#666;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;*overflow:visible}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type=datetime-local]{padding:.34375em .625em}input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type=checkbox],input[type=radio],input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}input[type=color]:focus,input[type=date]:focus,input[type=datetime-local]:focus,input[type=datetime]:focus,input[type=email]:focus,input[type=month]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=time]:focus,input[type=url]:focus,input[type=week]:focus{outline:0;outline:thin dotted\9;border-color:#333}input.no-focus:focus{border-color:#ccc!important}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type=color][disabled],input[type=date][disabled],input[type=datetime-local][disabled],input[type=datetime][disabled],input[type=email][disabled],input[type=month][disabled],input[type=number][disabled],input[type=password][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=text][disabled],input[type=time][disabled],input[type=url][disabled],input[type=week][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,select:focus:invalid,textarea:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,select:focus:invalid:focus,textarea:focus:invalid:focus{border-color:#e74c3c}input[type=checkbox]:focus:invalid:focus,input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}input[readonly],select[disabled],select[readonly],textarea[disabled],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type=checkbox][disabled],input[type=radio][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:1px solid #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{left:0;top:0;width:36px;height:12px;background:#ccc}.wy-switch:after,.wy-switch:before{position:absolute;content:"";display:block;border-radius:4px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{width:18px;height:18px;background:#999;left:-3px;top:-3px}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27ae60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type=color],.wy-control-group.wy-control-group-error input[type=date],.wy-control-group.wy-control-group-error input[type=datetime-local],.wy-control-group.wy-control-group-error input[type=datetime],.wy-control-group.wy-control-group-error input[type=email],.wy-control-group.wy-control-group-error input[type=month],.wy-control-group.wy-control-group-error input[type=number],.wy-control-group.wy-control-group-error input[type=password],.wy-control-group.wy-control-group-error input[type=search],.wy-control-group.wy-control-group-error input[type=tel],.wy-control-group.wy-control-group-error input[type=text],.wy-control-group.wy-control-group-error input[type=time],.wy-control-group.wy-control-group-error input[type=url],.wy-control-group.wy-control-group-error input[type=week],.wy-control-group.wy-control-group-error textarea{border:1px solid #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width:480px){.wy-form button[type=submit]{margin:.7em 0 0}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=text],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week],.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0}.wy-form-message,.wy-form-message-inline,.wy-form .wy-help-inline{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width:768px){.tablet-hide{display:none}}@media screen and (max-width:480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.rst-content table.docutils,.rst-content table.field-list,.wy-table{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.rst-content table.docutils caption,.rst-content table.field-list caption,.wy-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.rst-content table.docutils td,.rst-content table.docutils th,.rst-content table.field-list td,.rst-content table.field-list th,.wy-table td,.wy-table th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.rst-content table.docutils td:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list td:first-child,.rst-content table.field-list th:first-child,.wy-table td:first-child,.wy-table th:first-child{border-left-width:0}.rst-content table.docutils thead,.rst-content table.field-list thead,.wy-table thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.rst-content table.docutils thead th,.rst-content table.field-list thead th,.wy-table thead th{font-weight:700;border-bottom:2px solid #e1e4e5}.rst-content table.docutils td,.rst-content table.field-list td,.wy-table td{background-color:transparent;vertical-align:middle}.rst-content table.docutils td p,.rst-content table.field-list td p,.wy-table td p{line-height:18px}.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child,.wy-table td p:last-child{margin-bottom:0}.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min,.wy-table .wy-table-cell-min{width:1%;padding-right:0}.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:grey;font-size:90%}.wy-table-tertiary{color:grey;font-size:80%}.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,.wy-table-backed,.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td{background-color:#f3f6f6}.rst-content table.docutils,.wy-table-bordered-all{border:1px solid #e1e4e5}.rst-content table.docutils td,.wy-table-bordered-all td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.rst-content table.docutils tbody>tr:last-child td,.wy-table-bordered-all tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0!important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%}body,html{overflow-x:hidden}body{font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-weight:400;color:#404040;min-height:100%;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22!important}a.wy-text-warning:hover{color:#eb9950!important}.wy-text-info{color:#2980b9!important}a.wy-text-info:hover{color:#409ad5!important}.wy-text-success{color:#27ae60!important}a.wy-text-success:hover{color:#36d278!important}.wy-text-danger{color:#e74c3c!important}a.wy-text-danger:hover{color:#ed7669!important}.wy-text-neutral{color:#404040!important}a.wy-text-neutral:hover{color:#595959!important}.rst-content .toctree-wrapper>p.caption,h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif}p{line-height:24px;font-size:16px;margin:0 0 24px}h1{font-size:175%}.rst-content .toctree-wrapper>p.caption,h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}.rst-content code,.rst-content tt,code{white-space:nowrap;max-width:100%;background:#fff;border:1px solid #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#e74c3c;overflow-x:auto}.rst-content tt.code-large,code.code-large{font-size:90%}.rst-content .section ul,.rst-content .toctree-wrapper ul,.rst-content section ul,.wy-plain-list-disc,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.rst-content .section ul li,.rst-content .toctree-wrapper ul li,.rst-content section ul li,.wy-plain-list-disc li,article ul li{list-style:disc;margin-left:24px}.rst-content .section ul li p:last-child,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li p:last-child,.rst-content .toctree-wrapper ul li ul,.rst-content section ul li p:last-child,.rst-content section ul li ul,.wy-plain-list-disc li p:last-child,.wy-plain-list-disc li ul,article ul li p:last-child,article ul li ul{margin-bottom:0}.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,.rst-content section ul li li,.wy-plain-list-disc li li,article ul li li{list-style:circle}.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,.rst-content section ul li li li,.wy-plain-list-disc li li li,article ul li li li{list-style:square}.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,.rst-content section ul li ol li,.wy-plain-list-disc li ol li,article ul li ol li{list-style:decimal}.rst-content .section ol,.rst-content .section ol.arabic,.rst-content .toctree-wrapper ol,.rst-content .toctree-wrapper ol.arabic,.rst-content section ol,.rst-content section ol.arabic,.wy-plain-list-decimal,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.rst-content .section ol.arabic li,.rst-content .section ol li,.rst-content .toctree-wrapper ol.arabic li,.rst-content .toctree-wrapper ol li,.rst-content section ol.arabic li,.rst-content section ol li,.wy-plain-list-decimal li,article ol li{list-style:decimal;margin-left:24px}.rst-content .section ol.arabic li ul,.rst-content .section ol li p:last-child,.rst-content .section ol li ul,.rst-content .toctree-wrapper ol.arabic li ul,.rst-content .toctree-wrapper ol li p:last-child,.rst-content .toctree-wrapper ol li ul,.rst-content section ol.arabic li ul,.rst-content section ol li p:last-child,.rst-content section ol li ul,.wy-plain-list-decimal li p:last-child,.wy-plain-list-decimal li ul,article ol li p:last-child,article ol li ul{margin-bottom:0}.rst-content .section ol.arabic li ul li,.rst-content .section ol li ul li,.rst-content .toctree-wrapper ol.arabic li ul li,.rst-content .toctree-wrapper ol li ul li,.rst-content section ol.arabic li ul li,.rst-content section ol li ul li,.wy-plain-list-decimal li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:after,.wy-breadcrumbs:before{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs>li{display:inline-block;padding-top:5px}.wy-breadcrumbs>li.wy-breadcrumbs-aside{float:right}.rst-content .wy-breadcrumbs>li code,.rst-content .wy-breadcrumbs>li tt,.wy-breadcrumbs>li .rst-content tt,.wy-breadcrumbs>li code{all:inherit;color:inherit}.breadcrumb-item:before{content:"/";color:#bbb;font-size:13px;padding:0 6px 0 3px}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width:480px){.wy-breadcrumbs-extra,.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:after,.wy-menu-horiz:before{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz li,.wy-menu-horiz ul{display:inline-block}.wy-menu-horiz li:hover{background:hsla(0,0%,100%,.1)}.wy-menu-horiz li.divide-left{border-left:1px solid #404040}.wy-menu-horiz li.divide-right{border-right:1px solid #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:#55a5d9;height:32px;line-height:32px;padding:0 1.618em;margin:12px 0 0;display:block;font-weight:700;text-transform:uppercase;font-size:85%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:1px solid #404040}.wy-menu-vertical li.divide-bottom{border-bottom:1px solid #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:grey;border-right:1px solid #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.rst-content .wy-menu-vertical li tt,.wy-menu-vertical li .rst-content tt,.wy-menu-vertical li code{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li button.toctree-expand{display:block;float:left;margin-left:-1.2em;line-height:18px;color:#4d4d4d;border:none;background:none;padding:0}.wy-menu-vertical li.current>a,.wy-menu-vertical li.on a{color:#404040;font-weight:700;position:relative;background:#fcfcfc;border:none;padding:.4045em 1.618em}.wy-menu-vertical li.current>a:hover,.wy-menu-vertical li.on a:hover{background:#fcfcfc}.wy-menu-vertical li.current>a:hover button.toctree-expand,.wy-menu-vertical li.on a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand{display:block;line-height:18px;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:1px solid #c9c9c9;border-top:1px solid #c9c9c9}.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .toctree-l11>ul{display:none}.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul{display:block}.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a,.wy-menu-vertical li.toctree-l5 a,.wy-menu-vertical li.toctree-l6 a,.wy-menu-vertical li.toctree-l7 a,.wy-menu-vertical li.toctree-l8 a,.wy-menu-vertical li.toctree-l9 a,.wy-menu-vertical li.toctree-l10 a{color:#404040}.wy-menu-vertical li.toctree-l2 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l3 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l4 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l5 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l6 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l7 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l8 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l9 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l10 a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a,.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a,.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a,.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a,.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a,.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a,.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a,.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{display:block}.wy-menu-vertical li.toctree-l2.current>a{padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{padding:.4045em 1.618em .4045em 4.045em}.wy-menu-vertical li.toctree-l3.current>a{padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{padding:.4045em 1.618em .4045em 5.663em}.wy-menu-vertical li.toctree-l4.current>a{padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a{padding:.4045em 1.618em .4045em 7.281em}.wy-menu-vertical li.toctree-l5.current>a{padding:.4045em 7.281em}.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a{padding:.4045em 1.618em .4045em 8.899em}.wy-menu-vertical li.toctree-l6.current>a{padding:.4045em 8.899em}.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a{padding:.4045em 1.618em .4045em 10.517em}.wy-menu-vertical li.toctree-l7.current>a{padding:.4045em 10.517em}.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a{padding:.4045em 1.618em .4045em 12.135em}.wy-menu-vertical li.toctree-l8.current>a{padding:.4045em 12.135em}.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a{padding:.4045em 1.618em .4045em 13.753em}.wy-menu-vertical li.toctree-l9.current>a{padding:.4045em 13.753em}.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a{padding:.4045em 1.618em .4045em 15.371em}.wy-menu-vertical li.toctree-l10.current>a{padding:.4045em 15.371em}.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{padding:.4045em 1.618em .4045em 16.989em}.wy-menu-vertical li.toctree-l2.current>a,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{background:#c9c9c9}.wy-menu-vertical li.toctree-l2 button.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3.current>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{background:#bdbdbd}.wy-menu-vertical li.toctree-l3 button.toctree-expand{color:#969696}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:400}.wy-menu-vertical a{line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover button.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-menu-vertical a:active button.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980b9;text-align:center;color:#fcfcfc}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search .wy-dropdown>a,.wy-side-nav-search>a{color:#fcfcfc;font-size:100%;font-weight:700;display:inline-block;padding:4px 6px;margin-bottom:.809em;max-width:100%}.wy-side-nav-search .wy-dropdown>a:hover,.wy-side-nav-search .wy-dropdown>aactive,.wy-side-nav-search .wy-dropdown>afocus,.wy-side-nav-search>a:hover,.wy-side-nav-search>aactive,.wy-side-nav-search>afocus{background:hsla(0,0%,100%,.1)}.wy-side-nav-search .wy-dropdown>a img.logo,.wy-side-nav-search>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search .wy-dropdown>a.icon,.wy-side-nav-search>a.icon{display:block}.wy-side-nav-search .wy-dropdown>a.icon img.logo,.wy-side-nav-search>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.switch-menus{position:relative;display:block;margin-top:-.4045em;margin-bottom:.809em;font-weight:400;color:hsla(0,0%,100%,.3)}.wy-side-nav-search>div.switch-menus>div.language-switch,.wy-side-nav-search>div.switch-menus>div.version-switch{display:inline-block;padding:.2em}.wy-side-nav-search>div.switch-menus>div.language-switch select,.wy-side-nav-search>div.switch-menus>div.version-switch select{display:inline-block;margin-right:-2rem;padding-right:2rem;max-width:240px;text-align-last:center;background:none;border:none;border-radius:0;box-shadow:none;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-size:1em;font-weight:400;color:hsla(0,0%,100%,.3);cursor:pointer;appearance:none;-webkit-appearance:none;-moz-appearance:none}.wy-side-nav-search>div.switch-menus>div.language-switch select:active,.wy-side-nav-search>div.switch-menus>div.language-switch select:focus,.wy-side-nav-search>div.switch-menus>div.language-switch select:hover,.wy-side-nav-search>div.switch-menus>div.version-switch select:active,.wy-side-nav-search>div.switch-menus>div.version-switch select:focus,.wy-side-nav-search>div.switch-menus>div.version-switch select:hover{background:hsla(0,0%,100%,.1);color:hsla(0,0%,100%,.5)}.wy-side-nav-search>div.switch-menus>div.language-switch select option,.wy-side-nav-search>div.switch-menus>div.version-switch select option{color:#000}.wy-side-nav-search>div.switch-menus>div.language-switch:has(>select):after,.wy-side-nav-search>div.switch-menus>div.version-switch:has(>select):after{display:inline-block;width:1.5em;height:100%;padding:.1em;content:"\f0d7";font-size:1em;line-height:1.2em;font-family:FontAwesome;text-align:center;pointer-events:none;box-sizing:border-box}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:after,.wy-nav-top:before{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:700}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:grey}footer p{margin-bottom:12px}.rst-content footer span.commit tt,footer span.commit .rst-content tt,footer span.commit code{padding:0;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:1em;background:none;border:none;color:grey}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:after,.rst-footer-buttons:before{width:100%;display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:after,.rst-breadcrumbs-buttons:before{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:1px solid #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:1px solid #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:grey;font-size:90%}.genindextable li>ul{margin-left:24px}@media screen and (max-width:768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-menu.wy-menu-vertical,.wy-side-nav-search,.wy-side-scroll{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width:1100px){.wy-nav-content-wrap{background:rgba(0,0,0,.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,.wy-nav-side,footer{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:after,.rst-versions .rst-current-version:before{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-content .eqno .rst-versions .rst-current-version .headerlink,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-content p .rst-versions .rst-current-version .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-versions .rst-current-version .rst-content .eqno .headerlink,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-versions .rst-current-version .rst-content p .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-versions .rst-current-version .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version button.toctree-expand{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions .rst-other-versions .rtd-current-item{font-weight:700}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}#flyout-search-form{padding:6px}.rst-content .toctree-wrapper>p.caption,.rst-content h1,.rst-content h2,.rst-content h3,.rst-content h4,.rst-content h5,.rst-content h6{margin-bottom:24px}.rst-content img{max-width:100%;height:auto}.rst-content div.figure,.rst-content figure{margin-bottom:24px}.rst-content div.figure .caption-text,.rst-content figure .caption-text{font-style:italic}.rst-content div.figure p:last-child.caption,.rst-content figure p:last-child.caption{margin-bottom:0}.rst-content div.figure.align-center,.rst-content figure.align-center{text-align:center}.rst-content .section>a>img,.rst-content .section>img,.rst-content section>a>img,.rst-content section>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"\f08e";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;display:block;overflow:auto}.rst-content div[class^=highlight],.rst-content pre.literal-block{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px}.rst-content div[class^=highlight] div[class^=highlight],.rst-content pre.literal-block div[class^=highlight]{padding:0;border:none;margin:0}.rst-content div[class^=highlight] td.code{width:100%}.rst-content .linenodiv pre{border-right:1px solid #e6e9ea;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^=highlight] pre{white-space:pre;margin:0;padding:12px;display:block;overflow:auto}.rst-content div[class^=highlight] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content .linenodiv pre,.rst-content div[class^=highlight] pre,.rst-content pre.literal-block{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:12px;line-height:1.4}.rst-content div.highlight .gp,.rst-content div.highlight span.linenos{user-select:none;pointer-events:none}.rst-content div.highlight span.linenos{display:inline-block;padding-left:0;padding-right:12px;margin-right:12px;border-right:1px solid #e6e9ea}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^=highlight],.rst-content div[class^=highlight] pre{white-space:pre-wrap}}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning{clear:both}.rst-content .admonition-todo .last,.rst-content .admonition-todo>:last-child,.rst-content .admonition .last,.rst-content .admonition>:last-child,.rst-content .attention .last,.rst-content .attention>:last-child,.rst-content .caution .last,.rst-content .caution>:last-child,.rst-content .danger .last,.rst-content .danger>:last-child,.rst-content .error .last,.rst-content .error>:last-child,.rst-content .hint .last,.rst-content .hint>:last-child,.rst-content .important .last,.rst-content .important>:last-child,.rst-content .note .last,.rst-content .note>:last-child,.rst-content .seealso .last,.rst-content .seealso>:last-child,.rst-content .tip .last,.rst-content .tip>:last-child,.rst-content .warning .last,.rst-content .warning>:last-child{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent!important;border-color:rgba(0,0,0,.1)!important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha>li,.rst-content .toctree-wrapper ol.loweralpha,.rst-content .toctree-wrapper ol.loweralpha>li,.rst-content section ol.loweralpha,.rst-content section ol.loweralpha>li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha>li,.rst-content .toctree-wrapper ol.upperalpha,.rst-content .toctree-wrapper ol.upperalpha>li,.rst-content section ol.upperalpha,.rst-content section ol.upperalpha>li{list-style:upper-alpha}.rst-content .section ol li>*,.rst-content .section ul li>*,.rst-content .toctree-wrapper ol li>*,.rst-content .toctree-wrapper ul li>*,.rst-content section ol li>*,.rst-content section ul li>*{margin-top:12px;margin-bottom:12px}.rst-content .section ol li>:first-child,.rst-content .section ul li>:first-child,.rst-content .toctree-wrapper ol li>:first-child,.rst-content .toctree-wrapper ul li>:first-child,.rst-content section ol li>:first-child,.rst-content section ul li>:first-child{margin-top:0}.rst-content .section ol li>p,.rst-content .section ol li>p:last-child,.rst-content .section ul li>p,.rst-content .section ul li>p:last-child,.rst-content .toctree-wrapper ol li>p,.rst-content .toctree-wrapper ol li>p:last-child,.rst-content .toctree-wrapper ul li>p,.rst-content .toctree-wrapper ul li>p:last-child,.rst-content section ol li>p,.rst-content section ol li>p:last-child,.rst-content section ul li>p,.rst-content section ul li>p:last-child{margin-bottom:12px}.rst-content .section ol li>p:only-child,.rst-content .section ol li>p:only-child:last-child,.rst-content .section ul li>p:only-child,.rst-content .section ul li>p:only-child:last-child,.rst-content .toctree-wrapper ol li>p:only-child,.rst-content .toctree-wrapper ol li>p:only-child:last-child,.rst-content .toctree-wrapper ul li>p:only-child,.rst-content .toctree-wrapper ul li>p:only-child:last-child,.rst-content section ol li>p:only-child,.rst-content section ol li>p:only-child:last-child,.rst-content section ul li>p:only-child,.rst-content section ul li>p:only-child:last-child{margin-bottom:0}.rst-content .section ol li>ol,.rst-content .section ol li>ul,.rst-content .section ul li>ol,.rst-content .section ul li>ul,.rst-content .toctree-wrapper ol li>ol,.rst-content .toctree-wrapper ol li>ul,.rst-content .toctree-wrapper ul li>ol,.rst-content .toctree-wrapper ul li>ul,.rst-content section ol li>ol,.rst-content section ol li>ul,.rst-content section ul li>ol,.rst-content section ul li>ul{margin-bottom:12px}.rst-content .section ol.simple li>*,.rst-content .section ol.simple li ol,.rst-content .section ol.simple li ul,.rst-content .section ul.simple li>*,.rst-content .section ul.simple li ol,.rst-content .section ul.simple li ul,.rst-content .toctree-wrapper ol.simple li>*,.rst-content .toctree-wrapper ol.simple li ol,.rst-content .toctree-wrapper ol.simple li ul,.rst-content .toctree-wrapper ul.simple li>*,.rst-content .toctree-wrapper ul.simple li ol,.rst-content .toctree-wrapper ul.simple li ul,.rst-content section ol.simple li>*,.rst-content section ol.simple li ol,.rst-content section ol.simple li ul,.rst-content section ul.simple li>*,.rst-content section ul.simple li ol,.rst-content section ul.simple li ul{margin-top:0;margin-bottom:0}.rst-content .line-block{margin-left:0;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0}.rst-content .topic-title{font-weight:700;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0 0 24px 24px}.rst-content .align-left{float:left;margin:0 24px 24px 0}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink{opacity:0;font-size:14px;font-family:FontAwesome;margin-left:.5em}.rst-content .code-block-caption .headerlink:focus,.rst-content .code-block-caption:hover .headerlink,.rst-content .eqno .headerlink:focus,.rst-content .eqno:hover .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink:focus,.rst-content .toctree-wrapper>p.caption:hover .headerlink,.rst-content dl dt .headerlink:focus,.rst-content dl dt:hover .headerlink,.rst-content h1 .headerlink:focus,.rst-content h1:hover .headerlink,.rst-content h2 .headerlink:focus,.rst-content h2:hover .headerlink,.rst-content h3 .headerlink:focus,.rst-content h3:hover .headerlink,.rst-content h4 .headerlink:focus,.rst-content h4:hover .headerlink,.rst-content h5 .headerlink:focus,.rst-content h5:hover .headerlink,.rst-content h6 .headerlink:focus,.rst-content h6:hover .headerlink,.rst-content p.caption .headerlink:focus,.rst-content p.caption:hover .headerlink,.rst-content p .headerlink:focus,.rst-content p:hover .headerlink,.rst-content table>caption .headerlink:focus,.rst-content table>caption:hover .headerlink{opacity:1}.rst-content p a{overflow-wrap:anywhere}.rst-content .wy-table td p,.rst-content .wy-table td ul,.rst-content .wy-table th p,.rst-content .wy-table th ul,.rst-content table.docutils td p,.rst-content table.docutils td ul,.rst-content table.docutils th p,.rst-content table.docutils th ul,.rst-content table.field-list td p,.rst-content table.field-list td ul,.rst-content table.field-list th p,.rst-content table.field-list th ul{font-size:inherit}.rst-content .btn:focus{outline:2px solid}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:1px solid #e1e4e5}.rst-content .sidebar dl,.rst-content .sidebar p,.rst-content .sidebar ul{font-size:90%}.rst-content .sidebar .last,.rst-content .sidebar>:last-child{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif;font-weight:700;background:#e1e4e5;padding:6px 12px;margin:-24px -24px 24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;box-shadow:0 0 0 2px #f1c40f;display:inline;font-weight:700}.rst-content .citation-reference,.rst-content .footnote-reference{vertical-align:baseline;position:relative;top:-.4em;line-height:0;font-size:90%}.rst-content .citation-reference>span.fn-bracket,.rst-content .footnote-reference>span.fn-bracket{display:none}.rst-content .hlist{width:100%}.rst-content dl dt span.classifier:before{content:" : "}.rst-content dl dt span.classifier-delimiter{display:none!important}html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.footnote{background:none;border:none}html.writer-html4 .rst-content table.docutils.citation td,html.writer-html4 .rst-content table.docutils.citation tr,html.writer-html4 .rst-content table.docutils.footnote td,html.writer-html4 .rst-content table.docutils.footnote tr{border:none;background-color:transparent!important;white-space:normal}html.writer-html4 .rst-content table.docutils.citation td.label,html.writer-html4 .rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{display:grid;grid-template-columns:auto minmax(80%,95%)}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{display:inline-grid;grid-template-columns:max-content auto}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{display:grid;grid-template-columns:auto auto minmax(.65rem,auto) minmax(40%,95%)}html.writer-html5 .rst-content aside.citation>span.label,html.writer-html5 .rst-content aside.footnote>span.label,html.writer-html5 .rst-content div.citation>span.label{grid-column-start:1;grid-column-end:2}html.writer-html5 .rst-content aside.citation>span.backrefs,html.writer-html5 .rst-content aside.footnote>span.backrefs,html.writer-html5 .rst-content div.citation>span.backrefs{grid-column-start:2;grid-column-end:3;grid-row-start:1;grid-row-end:3}html.writer-html5 .rst-content aside.citation>p,html.writer-html5 .rst-content aside.footnote>p,html.writer-html5 .rst-content div.citation>p{grid-column-start:4;grid-column-end:5}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{margin-bottom:24px}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{padding-left:1rem}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dd,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dd,html.writer-html5 .rst-content dl.footnote>dt{margin-bottom:0}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{font-size:.9rem}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.footnote>dt{margin:0 .5rem .5rem 0;line-height:1.2rem;word-break:break-all;font-weight:400}html.writer-html5 .rst-content dl.citation>dt>span.brackets:before,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before{content:"["}html.writer-html5 .rst-content dl.citation>dt>span.brackets:after,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after{content:"]"}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a{word-break:keep-all}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a:not(:first-child):before,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.footnote>dd{margin:0 0 .5rem;line-height:1.2rem}html.writer-html5 .rst-content dl.citation>dd p,html.writer-html5 .rst-content dl.footnote>dd p{font-size:.9rem}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{padding-left:1rem;padding-right:1rem;font-size:.9rem;line-height:1.2rem}html.writer-html5 .rst-content aside.citation p,html.writer-html5 .rst-content aside.footnote p,html.writer-html5 .rst-content div.citation p{font-size:.9rem;line-height:1.2rem;margin-bottom:12px}html.writer-html5 .rst-content aside.citation span.backrefs,html.writer-html5 .rst-content aside.footnote span.backrefs,html.writer-html5 .rst-content div.citation span.backrefs{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content aside.citation span.backrefs>a,html.writer-html5 .rst-content aside.footnote span.backrefs>a,html.writer-html5 .rst-content div.citation span.backrefs>a{word-break:keep-all}html.writer-html5 .rst-content aside.citation span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content aside.footnote span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content div.citation span.backrefs>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content aside.citation span.label,html.writer-html5 .rst-content aside.footnote span.label,html.writer-html5 .rst-content div.citation span.label{line-height:1.2rem}html.writer-html5 .rst-content aside.citation-list,html.writer-html5 .rst-content aside.footnote-list,html.writer-html5 .rst-content div.citation-list{margin-bottom:24px}html.writer-html5 .rst-content dl.option-list kbd{font-size:.9rem}.rst-content table.docutils.footnote,html.writer-html4 .rst-content table.docutils.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content aside.footnote-list aside.footnote,html.writer-html5 .rst-content div.citation-list>div.citation,html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{color:grey}.rst-content table.docutils.footnote code,.rst-content table.docutils.footnote tt,html.writer-html4 .rst-content table.docutils.citation code,html.writer-html4 .rst-content table.docutils.citation tt,html.writer-html5 .rst-content aside.footnote-list aside.footnote code,html.writer-html5 .rst-content aside.footnote-list aside.footnote tt,html.writer-html5 .rst-content aside.footnote code,html.writer-html5 .rst-content aside.footnote tt,html.writer-html5 .rst-content div.citation-list>div.citation code,html.writer-html5 .rst-content div.citation-list>div.citation tt,html.writer-html5 .rst-content dl.citation code,html.writer-html5 .rst-content dl.citation tt,html.writer-html5 .rst-content dl.footnote code,html.writer-html5 .rst-content dl.footnote tt{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}html.writer-html5 .rst-content table.docutils th{border:1px solid #e1e4e5}html.writer-html5 .rst-content table.docutils td>p,html.writer-html5 .rst-content table.docutils th>p{line-height:1rem;margin-bottom:0;font-size:.9rem}.rst-content table.docutils td .last,.rst-content table.docutils td .last>:last-child{margin-bottom:0}.rst-content table.field-list,.rst-content table.field-list td{border:none}.rst-content table.field-list td p{line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content code,.rst-content tt{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;padding:2px 5px}.rst-content code big,.rst-content code em,.rst-content tt big,.rst-content tt em{font-size:100%!important;line-height:normal}.rst-content code.literal,.rst-content tt.literal{color:#e74c3c;white-space:normal}.rst-content code.xref,.rst-content tt.xref,a .rst-content code,a .rst-content tt{font-weight:700;color:#404040;overflow-wrap:normal}.rst-content kbd,.rst-content pre,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace}.rst-content a code,.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:700;margin-bottom:12px}.rst-content dl ol,.rst-content dl p,.rst-content dl table,.rst-content dl ul{margin-bottom:12px}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}.rst-content dl dd>ol:last-child,.rst-content dl dd>p:last-child,.rst-content dl dd>table:last-child,.rst-content dl dd>ul:last-child{margin-bottom:0}html.writer-html4 .rst-content dl:not(.docutils),html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple){margin-bottom:24px}html.writer-html4 .rst-content dl:not(.docutils)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:3px solid #6ab0de;padding:6px;position:relative}html.writer-html4 .rst-content dl:not(.docutils)>dt:before,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:before{color:#6ab0de}html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{margin-bottom:6px;border:none;border-left:3px solid #ccc;background:#f0f0f0;color:#555}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:first-child{margin-top:0}html.writer-html4 .rst-content dl:not(.docutils) code.descclassname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{background-color:transparent;border:none;padding:0;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .optional,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .property{display:inline-block;padding-right:8px;max-width:100%}html.writer-html4 .rst-content dl:not(.docutils) .k,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .k{font-style:italic}html.writer-html4 .rst-content dl:not(.docutils) .descclassname,html.writer-html4 .rst-content dl:not(.docutils) .descname,html.writer-html4 .rst-content dl:not(.docutils) .sig-name,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .sig-name{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#000}.rst-content .viewcode-back,.rst-content .viewcode-link{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:700}.rst-content code.download,.rst-content tt.download{background:inherit;padding:inherit;font-weight:400;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content code.download span:first-child,.rst-content tt.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{margin-right:4px}.rst-content .guilabel,.rst-content .menuselection{font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content .guilabel,.rst-content .menuselection{border:1px solid #7fbbe3;background:#e7f2fa}.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>.kbd,.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>kbd{color:inherit;font-size:80%;background-color:#fff;border:1px solid #a6a6a6;border-radius:4px;box-shadow:0 2px grey;padding:2.4px 6px;margin:auto 0}.rst-content .versionmodified{font-style:italic}@media screen and (max-width:480px){.rst-content .sidebar{width:100%;float:none;margin-left:0}}span[id*=MathJax-Span]{color:#404040}.math{text-align:center}@font-face{font-family:Lato;src:url(fonts/lato-normal.woff2?bd03a2cc277bbbc338d464e679fe9942) format("woff2"),url(fonts/lato-normal.woff?27bd77b9162d388cb8d4c4217c7c5e2a) format("woff");font-weight:400;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold.woff2?cccb897485813c7c256901dbca54ecf2) format("woff2"),url(fonts/lato-bold.woff?d878b6c29b10beca227e9eef4246111b) format("woff");font-weight:700;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold-italic.woff2?0b6bb6725576b072c5d0b02ecdd1900d) format("woff2"),url(fonts/lato-bold-italic.woff?9c7e4e9eb485b4a121c760e61bc3707c) format("woff");font-weight:700;font-style:italic;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-normal-italic.woff2?4eb103b4d12be57cb1d040ed5e162e9d) format("woff2"),url(fonts/lato-normal-italic.woff?f28f2d6482446544ef1ea1ccc6dd5892) format("woff");font-weight:400;font-style:italic;font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:400;src:url(fonts/Roboto-Slab-Regular.woff2?7abf5b8d04d26a2cafea937019bca958) format("woff2"),url(fonts/Roboto-Slab-Regular.woff?c1be9284088d487c5e3ff0a10a92e58c) format("woff");font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:700;src:url(fonts/Roboto-Slab-Bold.woff2?9984f4a9bda09be08e83f2506954adbe) format("woff2"),url(fonts/Roboto-Slab-Bold.woff?bed5564a116b05148e3b3bea6fb1162a) format("woff");font-display:block} \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_static/doctools.js b/src/scitex/_sphinx_html/_static/doctools.js new file mode 100644 index 000000000..807cdb176 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/doctools.js @@ -0,0 +1,150 @@ +/* + * Base JavaScript utilities for all Sphinx HTML documentation. + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})`, + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)), + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS + && !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) + return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/src/scitex/_sphinx_html/_static/documentation_options.js b/src/scitex/_sphinx_html/_static/documentation_options.js new file mode 100644 index 000000000..976fa38d6 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '0.0.0', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_static/english-stemmer.js b/src/scitex/_sphinx_html/_static/english-stemmer.js new file mode 100644 index 000000000..056760ee8 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/english-stemmer.js @@ -0,0 +1,1066 @@ +// Generated from english.sbl by Snowball 3.0.1 - https://snowballstem.org/ + +/**@constructor*/ +var EnglishStemmer = function() { + var base = new BaseStemmer(); + + /** @const */ var a_0 = [ + ["arsen", -1, -1], + ["commun", -1, -1], + ["emerg", -1, -1], + ["gener", -1, -1], + ["later", -1, -1], + ["organ", -1, -1], + ["past", -1, -1], + ["univers", -1, -1] + ]; + + /** @const */ var a_1 = [ + ["'", -1, 1], + ["'s'", 0, 1], + ["'s", -1, 1] + ]; + + /** @const */ var a_2 = [ + ["ied", -1, 2], + ["s", -1, 3], + ["ies", 1, 2], + ["sses", 1, 1], + ["ss", 1, -1], + ["us", 1, -1] + ]; + + /** @const */ var a_3 = [ + ["succ", -1, 1], + ["proc", -1, 1], + ["exc", -1, 1] + ]; + + /** @const */ var a_4 = [ + ["even", -1, 2], + ["cann", -1, 2], + ["inn", -1, 2], + ["earr", -1, 2], + ["herr", -1, 2], + ["out", -1, 2], + ["y", -1, 1] + ]; + + /** @const */ var a_5 = [ + ["", -1, -1], + ["ed", 0, 2], + ["eed", 1, 1], + ["ing", 0, 3], + ["edly", 0, 2], + ["eedly", 4, 1], + ["ingly", 0, 2] + ]; + + /** @const */ var a_6 = [ + ["", -1, 3], + ["bb", 0, 2], + ["dd", 0, 2], + ["ff", 0, 2], + ["gg", 0, 2], + ["bl", 0, 1], + ["mm", 0, 2], + ["nn", 0, 2], + ["pp", 0, 2], + ["rr", 0, 2], + ["at", 0, 1], + ["tt", 0, 2], + ["iz", 0, 1] + ]; + + /** @const */ var a_7 = [ + ["anci", -1, 3], + ["enci", -1, 2], + ["ogi", -1, 14], + ["li", -1, 16], + ["bli", 3, 12], + ["abli", 4, 4], + ["alli", 3, 8], + ["fulli", 3, 9], + ["lessli", 3, 15], + ["ousli", 3, 10], + ["entli", 3, 5], + ["aliti", -1, 8], + ["biliti", -1, 12], + ["iviti", -1, 11], + ["tional", -1, 1], + ["ational", 14, 7], + ["alism", -1, 8], + ["ation", -1, 7], + ["ization", 17, 6], + ["izer", -1, 6], + ["ator", -1, 7], + ["iveness", -1, 11], + ["fulness", -1, 9], + ["ousness", -1, 10], + ["ogist", -1, 13] + ]; + + /** @const */ var a_8 = [ + ["icate", -1, 4], + ["ative", -1, 6], + ["alize", -1, 3], + ["iciti", -1, 4], + ["ical", -1, 4], + ["tional", -1, 1], + ["ational", 5, 2], + ["ful", -1, 5], + ["ness", -1, 5] + ]; + + /** @const */ var a_9 = [ + ["ic", -1, 1], + ["ance", -1, 1], + ["ence", -1, 1], + ["able", -1, 1], + ["ible", -1, 1], + ["ate", -1, 1], + ["ive", -1, 1], + ["ize", -1, 1], + ["iti", -1, 1], + ["al", -1, 1], + ["ism", -1, 1], + ["ion", -1, 2], + ["er", -1, 1], + ["ous", -1, 1], + ["ant", -1, 1], + ["ent", -1, 1], + ["ment", 15, 1], + ["ement", 16, 1] + ]; + + /** @const */ var a_10 = [ + ["e", -1, 1], + ["l", -1, 2] + ]; + + /** @const */ var a_11 = [ + ["andes", -1, -1], + ["atlas", -1, -1], + ["bias", -1, -1], + ["cosmos", -1, -1], + ["early", -1, 5], + ["gently", -1, 3], + ["howe", -1, -1], + ["idly", -1, 2], + ["news", -1, -1], + ["only", -1, 6], + ["singly", -1, 7], + ["skies", -1, 1], + ["sky", -1, -1], + ["ugly", -1, 4] + ]; + + /** @const */ var /** Array */ g_aeo = [17, 64]; + + /** @const */ var /** Array */ g_v = [17, 65, 16, 1]; + + /** @const */ var /** Array */ g_v_WXY = [1, 17, 65, 208, 1]; + + /** @const */ var /** Array */ g_valid_LI = [55, 141, 2]; + + var /** boolean */ B_Y_found = false; + var /** number */ I_p2 = 0; + var /** number */ I_p1 = 0; + + + /** @return {boolean} */ + function r_prelude() { + B_Y_found = false; + /** @const */ var /** number */ v_1 = base.cursor; + lab0: { + base.bra = base.cursor; + if (!(base.eq_s("'"))) + { + break lab0; + } + base.ket = base.cursor; + if (!base.slice_del()) + { + return false; + } + } + base.cursor = v_1; + /** @const */ var /** number */ v_2 = base.cursor; + lab1: { + base.bra = base.cursor; + if (!(base.eq_s("y"))) + { + break lab1; + } + base.ket = base.cursor; + if (!base.slice_from("Y")) + { + return false; + } + B_Y_found = true; + } + base.cursor = v_2; + /** @const */ var /** number */ v_3 = base.cursor; + lab2: { + while(true) + { + /** @const */ var /** number */ v_4 = base.cursor; + lab3: { + golab4: while(true) + { + /** @const */ var /** number */ v_5 = base.cursor; + lab5: { + if (!(base.in_grouping(g_v, 97, 121))) + { + break lab5; + } + base.bra = base.cursor; + if (!(base.eq_s("y"))) + { + break lab5; + } + base.ket = base.cursor; + base.cursor = v_5; + break golab4; + } + base.cursor = v_5; + if (base.cursor >= base.limit) + { + break lab3; + } + base.cursor++; + } + if (!base.slice_from("Y")) + { + return false; + } + B_Y_found = true; + continue; + } + base.cursor = v_4; + break; + } + } + base.cursor = v_3; + return true; + }; + + /** @return {boolean} */ + function r_mark_regions() { + I_p1 = base.limit; + I_p2 = base.limit; + /** @const */ var /** number */ v_1 = base.cursor; + lab0: { + lab1: { + /** @const */ var /** number */ v_2 = base.cursor; + lab2: { + if (base.find_among(a_0) == 0) + { + break lab2; + } + break lab1; + } + base.cursor = v_2; + if (!base.go_out_grouping(g_v, 97, 121)) + { + break lab0; + } + base.cursor++; + if (!base.go_in_grouping(g_v, 97, 121)) + { + break lab0; + } + base.cursor++; + } + I_p1 = base.cursor; + if (!base.go_out_grouping(g_v, 97, 121)) + { + break lab0; + } + base.cursor++; + if (!base.go_in_grouping(g_v, 97, 121)) + { + break lab0; + } + base.cursor++; + I_p2 = base.cursor; + } + base.cursor = v_1; + return true; + }; + + /** @return {boolean} */ + function r_shortv() { + lab0: { + /** @const */ var /** number */ v_1 = base.limit - base.cursor; + lab1: { + if (!(base.out_grouping_b(g_v_WXY, 89, 121))) + { + break lab1; + } + if (!(base.in_grouping_b(g_v, 97, 121))) + { + break lab1; + } + if (!(base.out_grouping_b(g_v, 97, 121))) + { + break lab1; + } + break lab0; + } + base.cursor = base.limit - v_1; + lab2: { + if (!(base.out_grouping_b(g_v, 97, 121))) + { + break lab2; + } + if (!(base.in_grouping_b(g_v, 97, 121))) + { + break lab2; + } + if (base.cursor > base.limit_backward) + { + break lab2; + } + break lab0; + } + base.cursor = base.limit - v_1; + if (!(base.eq_s_b("past"))) + { + return false; + } + } + return true; + }; + + /** @return {boolean} */ + function r_R1() { + return I_p1 <= base.cursor; + }; + + /** @return {boolean} */ + function r_R2() { + return I_p2 <= base.cursor; + }; + + /** @return {boolean} */ + function r_Step_1a() { + var /** number */ among_var; + /** @const */ var /** number */ v_1 = base.limit - base.cursor; + lab0: { + base.ket = base.cursor; + if (base.find_among_b(a_1) == 0) + { + base.cursor = base.limit - v_1; + break lab0; + } + base.bra = base.cursor; + if (!base.slice_del()) + { + return false; + } + } + base.ket = base.cursor; + among_var = base.find_among_b(a_2); + if (among_var == 0) + { + return false; + } + base.bra = base.cursor; + switch (among_var) { + case 1: + if (!base.slice_from("ss")) + { + return false; + } + break; + case 2: + lab1: { + /** @const */ var /** number */ v_2 = base.limit - base.cursor; + lab2: { + { + /** @const */ var /** number */ c1 = base.cursor - 2; + if (c1 < base.limit_backward) + { + break lab2; + } + base.cursor = c1; + } + if (!base.slice_from("i")) + { + return false; + } + break lab1; + } + base.cursor = base.limit - v_2; + if (!base.slice_from("ie")) + { + return false; + } + } + break; + case 3: + if (base.cursor <= base.limit_backward) + { + return false; + } + base.cursor--; + if (!base.go_out_grouping_b(g_v, 97, 121)) + { + return false; + } + base.cursor--; + if (!base.slice_del()) + { + return false; + } + break; + } + return true; + }; + + /** @return {boolean} */ + function r_Step_1b() { + var /** number */ among_var; + base.ket = base.cursor; + among_var = base.find_among_b(a_5); + base.bra = base.cursor; + lab0: { + /** @const */ var /** number */ v_1 = base.limit - base.cursor; + lab1: { + switch (among_var) { + case 1: + /** @const */ var /** number */ v_2 = base.limit - base.cursor; + lab2: { + lab3: { + /** @const */ var /** number */ v_3 = base.limit - base.cursor; + lab4: { + if (base.find_among_b(a_3) == 0) + { + break lab4; + } + if (base.cursor > base.limit_backward) + { + break lab4; + } + break lab3; + } + base.cursor = base.limit - v_3; + if (!r_R1()) + { + break lab2; + } + if (!base.slice_from("ee")) + { + return false; + } + } + } + base.cursor = base.limit - v_2; + break; + case 2: + break lab1; + case 3: + among_var = base.find_among_b(a_4); + if (among_var == 0) + { + break lab1; + } + switch (among_var) { + case 1: + /** @const */ var /** number */ v_4 = base.limit - base.cursor; + if (!(base.out_grouping_b(g_v, 97, 121))) + { + break lab1; + } + if (base.cursor > base.limit_backward) + { + break lab1; + } + base.cursor = base.limit - v_4; + base.bra = base.cursor; + if (!base.slice_from("ie")) + { + return false; + } + break; + case 2: + if (base.cursor > base.limit_backward) + { + break lab1; + } + break; + } + break; + } + break lab0; + } + base.cursor = base.limit - v_1; + /** @const */ var /** number */ v_5 = base.limit - base.cursor; + if (!base.go_out_grouping_b(g_v, 97, 121)) + { + return false; + } + base.cursor--; + base.cursor = base.limit - v_5; + if (!base.slice_del()) + { + return false; + } + base.ket = base.cursor; + base.bra = base.cursor; + /** @const */ var /** number */ v_6 = base.limit - base.cursor; + among_var = base.find_among_b(a_6); + switch (among_var) { + case 1: + if (!base.slice_from("e")) + { + return false; + } + return false; + case 2: + { + /** @const */ var /** number */ v_7 = base.limit - base.cursor; + lab5: { + if (!(base.in_grouping_b(g_aeo, 97, 111))) + { + break lab5; + } + if (base.cursor > base.limit_backward) + { + break lab5; + } + return false; + } + base.cursor = base.limit - v_7; + } + break; + case 3: + if (base.cursor != I_p1) + { + return false; + } + /** @const */ var /** number */ v_8 = base.limit - base.cursor; + if (!r_shortv()) + { + return false; + } + base.cursor = base.limit - v_8; + if (!base.slice_from("e")) + { + return false; + } + return false; + } + base.cursor = base.limit - v_6; + base.ket = base.cursor; + if (base.cursor <= base.limit_backward) + { + return false; + } + base.cursor--; + base.bra = base.cursor; + if (!base.slice_del()) + { + return false; + } + } + return true; + }; + + /** @return {boolean} */ + function r_Step_1c() { + base.ket = base.cursor; + lab0: { + /** @const */ var /** number */ v_1 = base.limit - base.cursor; + lab1: { + if (!(base.eq_s_b("y"))) + { + break lab1; + } + break lab0; + } + base.cursor = base.limit - v_1; + if (!(base.eq_s_b("Y"))) + { + return false; + } + } + base.bra = base.cursor; + if (!(base.out_grouping_b(g_v, 97, 121))) + { + return false; + } + lab2: { + if (base.cursor > base.limit_backward) + { + break lab2; + } + return false; + } + if (!base.slice_from("i")) + { + return false; + } + return true; + }; + + /** @return {boolean} */ + function r_Step_2() { + var /** number */ among_var; + base.ket = base.cursor; + among_var = base.find_among_b(a_7); + if (among_var == 0) + { + return false; + } + base.bra = base.cursor; + if (!r_R1()) + { + return false; + } + switch (among_var) { + case 1: + if (!base.slice_from("tion")) + { + return false; + } + break; + case 2: + if (!base.slice_from("ence")) + { + return false; + } + break; + case 3: + if (!base.slice_from("ance")) + { + return false; + } + break; + case 4: + if (!base.slice_from("able")) + { + return false; + } + break; + case 5: + if (!base.slice_from("ent")) + { + return false; + } + break; + case 6: + if (!base.slice_from("ize")) + { + return false; + } + break; + case 7: + if (!base.slice_from("ate")) + { + return false; + } + break; + case 8: + if (!base.slice_from("al")) + { + return false; + } + break; + case 9: + if (!base.slice_from("ful")) + { + return false; + } + break; + case 10: + if (!base.slice_from("ous")) + { + return false; + } + break; + case 11: + if (!base.slice_from("ive")) + { + return false; + } + break; + case 12: + if (!base.slice_from("ble")) + { + return false; + } + break; + case 13: + if (!base.slice_from("og")) + { + return false; + } + break; + case 14: + if (!(base.eq_s_b("l"))) + { + return false; + } + if (!base.slice_from("og")) + { + return false; + } + break; + case 15: + if (!base.slice_from("less")) + { + return false; + } + break; + case 16: + if (!(base.in_grouping_b(g_valid_LI, 99, 116))) + { + return false; + } + if (!base.slice_del()) + { + return false; + } + break; + } + return true; + }; + + /** @return {boolean} */ + function r_Step_3() { + var /** number */ among_var; + base.ket = base.cursor; + among_var = base.find_among_b(a_8); + if (among_var == 0) + { + return false; + } + base.bra = base.cursor; + if (!r_R1()) + { + return false; + } + switch (among_var) { + case 1: + if (!base.slice_from("tion")) + { + return false; + } + break; + case 2: + if (!base.slice_from("ate")) + { + return false; + } + break; + case 3: + if (!base.slice_from("al")) + { + return false; + } + break; + case 4: + if (!base.slice_from("ic")) + { + return false; + } + break; + case 5: + if (!base.slice_del()) + { + return false; + } + break; + case 6: + if (!r_R2()) + { + return false; + } + if (!base.slice_del()) + { + return false; + } + break; + } + return true; + }; + + /** @return {boolean} */ + function r_Step_4() { + var /** number */ among_var; + base.ket = base.cursor; + among_var = base.find_among_b(a_9); + if (among_var == 0) + { + return false; + } + base.bra = base.cursor; + if (!r_R2()) + { + return false; + } + switch (among_var) { + case 1: + if (!base.slice_del()) + { + return false; + } + break; + case 2: + lab0: { + /** @const */ var /** number */ v_1 = base.limit - base.cursor; + lab1: { + if (!(base.eq_s_b("s"))) + { + break lab1; + } + break lab0; + } + base.cursor = base.limit - v_1; + if (!(base.eq_s_b("t"))) + { + return false; + } + } + if (!base.slice_del()) + { + return false; + } + break; + } + return true; + }; + + /** @return {boolean} */ + function r_Step_5() { + var /** number */ among_var; + base.ket = base.cursor; + among_var = base.find_among_b(a_10); + if (among_var == 0) + { + return false; + } + base.bra = base.cursor; + switch (among_var) { + case 1: + lab0: { + lab1: { + if (!r_R2()) + { + break lab1; + } + break lab0; + } + if (!r_R1()) + { + return false; + } + { + /** @const */ var /** number */ v_1 = base.limit - base.cursor; + lab2: { + if (!r_shortv()) + { + break lab2; + } + return false; + } + base.cursor = base.limit - v_1; + } + } + if (!base.slice_del()) + { + return false; + } + break; + case 2: + if (!r_R2()) + { + return false; + } + if (!(base.eq_s_b("l"))) + { + return false; + } + if (!base.slice_del()) + { + return false; + } + break; + } + return true; + }; + + /** @return {boolean} */ + function r_exception1() { + var /** number */ among_var; + base.bra = base.cursor; + among_var = base.find_among(a_11); + if (among_var == 0) + { + return false; + } + base.ket = base.cursor; + if (base.cursor < base.limit) + { + return false; + } + switch (among_var) { + case 1: + if (!base.slice_from("sky")) + { + return false; + } + break; + case 2: + if (!base.slice_from("idl")) + { + return false; + } + break; + case 3: + if (!base.slice_from("gentl")) + { + return false; + } + break; + case 4: + if (!base.slice_from("ugli")) + { + return false; + } + break; + case 5: + if (!base.slice_from("earli")) + { + return false; + } + break; + case 6: + if (!base.slice_from("onli")) + { + return false; + } + break; + case 7: + if (!base.slice_from("singl")) + { + return false; + } + break; + } + return true; + }; + + /** @return {boolean} */ + function r_postlude() { + if (!B_Y_found) + { + return false; + } + while(true) + { + /** @const */ var /** number */ v_1 = base.cursor; + lab0: { + golab1: while(true) + { + /** @const */ var /** number */ v_2 = base.cursor; + lab2: { + base.bra = base.cursor; + if (!(base.eq_s("Y"))) + { + break lab2; + } + base.ket = base.cursor; + base.cursor = v_2; + break golab1; + } + base.cursor = v_2; + if (base.cursor >= base.limit) + { + break lab0; + } + base.cursor++; + } + if (!base.slice_from("y")) + { + return false; + } + continue; + } + base.cursor = v_1; + break; + } + return true; + }; + + this.stem = /** @return {boolean} */ function() { + lab0: { + /** @const */ var /** number */ v_1 = base.cursor; + lab1: { + if (!r_exception1()) + { + break lab1; + } + break lab0; + } + base.cursor = v_1; + lab2: { + { + /** @const */ var /** number */ v_2 = base.cursor; + lab3: { + { + /** @const */ var /** number */ c1 = base.cursor + 3; + if (c1 > base.limit) + { + break lab3; + } + base.cursor = c1; + } + break lab2; + } + base.cursor = v_2; + } + break lab0; + } + base.cursor = v_1; + r_prelude(); + r_mark_regions(); + base.limit_backward = base.cursor; base.cursor = base.limit; + /** @const */ var /** number */ v_3 = base.limit - base.cursor; + r_Step_1a(); + base.cursor = base.limit - v_3; + /** @const */ var /** number */ v_4 = base.limit - base.cursor; + r_Step_1b(); + base.cursor = base.limit - v_4; + /** @const */ var /** number */ v_5 = base.limit - base.cursor; + r_Step_1c(); + base.cursor = base.limit - v_5; + /** @const */ var /** number */ v_6 = base.limit - base.cursor; + r_Step_2(); + base.cursor = base.limit - v_6; + /** @const */ var /** number */ v_7 = base.limit - base.cursor; + r_Step_3(); + base.cursor = base.limit - v_7; + /** @const */ var /** number */ v_8 = base.limit - base.cursor; + r_Step_4(); + base.cursor = base.limit - v_8; + /** @const */ var /** number */ v_9 = base.limit - base.cursor; + r_Step_5(); + base.cursor = base.limit - v_9; + base.cursor = base.limit_backward; + /** @const */ var /** number */ v_10 = base.cursor; + r_postlude(); + base.cursor = v_10; + } + return true; + }; + + /**@return{string}*/ + this['stemWord'] = function(/**string*/word) { + base.setCurrent(word); + this.stem(); + return base.getCurrent(); + }; +}; diff --git a/src/scitex/_sphinx_html/_static/fonts/Lato/lato-italic.eot b/src/scitex/_sphinx_html/_static/fonts/Lato/lato-italic.eot new file mode 100644 index 000000000..3f826421a Binary files /dev/null and b/src/scitex/_sphinx_html/_static/fonts/Lato/lato-italic.eot differ diff --git a/src/scitex/_sphinx_html/_static/fonts/Lato/lato-italic.ttf b/src/scitex/_sphinx_html/_static/fonts/Lato/lato-italic.ttf new file mode 100644 index 000000000..b4bfc9b24 Binary files /dev/null and b/src/scitex/_sphinx_html/_static/fonts/Lato/lato-italic.ttf differ diff --git a/src/scitex/_sphinx_html/_static/fonts/Lato/lato-italic.woff b/src/scitex/_sphinx_html/_static/fonts/Lato/lato-italic.woff new file mode 100644 index 000000000..76114bc03 Binary files /dev/null and b/src/scitex/_sphinx_html/_static/fonts/Lato/lato-italic.woff differ diff --git a/src/scitex/_sphinx_html/_static/fonts/Lato/lato-italic.woff2 b/src/scitex/_sphinx_html/_static/fonts/Lato/lato-italic.woff2 new file mode 100644 index 000000000..3404f37e2 Binary files /dev/null and b/src/scitex/_sphinx_html/_static/fonts/Lato/lato-italic.woff2 differ diff --git a/src/scitex/_sphinx_html/_static/fonts/Lato/lato-regular.eot b/src/scitex/_sphinx_html/_static/fonts/Lato/lato-regular.eot new file mode 100644 index 000000000..11e3f2a5f Binary files /dev/null and b/src/scitex/_sphinx_html/_static/fonts/Lato/lato-regular.eot differ diff --git a/src/scitex/_sphinx_html/_static/fonts/Lato/lato-regular.ttf b/src/scitex/_sphinx_html/_static/fonts/Lato/lato-regular.ttf new file mode 100644 index 000000000..74decd9eb Binary files /dev/null and b/src/scitex/_sphinx_html/_static/fonts/Lato/lato-regular.ttf differ diff --git a/src/scitex/_sphinx_html/_static/fonts/Lato/lato-regular.woff b/src/scitex/_sphinx_html/_static/fonts/Lato/lato-regular.woff new file mode 100644 index 000000000..ae1307ff5 Binary files /dev/null and b/src/scitex/_sphinx_html/_static/fonts/Lato/lato-regular.woff differ diff --git a/src/scitex/_sphinx_html/_static/fonts/Lato/lato-regular.woff2 b/src/scitex/_sphinx_html/_static/fonts/Lato/lato-regular.woff2 new file mode 100644 index 000000000..3bf984332 Binary files /dev/null and b/src/scitex/_sphinx_html/_static/fonts/Lato/lato-regular.woff2 differ diff --git a/src/scitex/_sphinx_html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot b/src/scitex/_sphinx_html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot new file mode 100644 index 000000000..2f7ca78a1 Binary files /dev/null and b/src/scitex/_sphinx_html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot differ diff --git a/src/scitex/_sphinx_html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf b/src/scitex/_sphinx_html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf new file mode 100644 index 000000000..eb52a7907 Binary files /dev/null and b/src/scitex/_sphinx_html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf differ diff --git a/src/scitex/_sphinx_html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff b/src/scitex/_sphinx_html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff new file mode 100644 index 000000000..f815f63f9 Binary files /dev/null and b/src/scitex/_sphinx_html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff differ diff --git a/src/scitex/_sphinx_html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 b/src/scitex/_sphinx_html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 new file mode 100644 index 000000000..f2c76e5bd Binary files /dev/null and b/src/scitex/_sphinx_html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 differ diff --git a/src/scitex/_sphinx_html/_static/gallery/area/fill_between.json b/src/scitex/_sphinx_html/_static/gallery/area/fill_between.json new file mode 100644 index 000000000..64fba97ed --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/area/fill_between.json @@ -0,0 +1,131 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:32.979662", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "fill_between", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Time", + "unit": "s", + "scale": "linear", + "lim": [ + -0.5, + 10.5 + ], + "n_ticks": 4 + }, + "y": { + "label": "Value", + "unit": "", + "scale": "linear", + "lim": [ + -1.6493384025373632, + 1.6496461858110392 + ], + "n_ticks": 4 + } + }, + "title": "ax.fill_between()", + "plot_type": "line", + "method": "plot", + "traces": [ + { + "id": "mean_line", + "color": "#0000ff", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_0_plot_x", + "y": "ax_00_plot_0_plot_y" + } + } + ] +} diff --git a/src/scitex/_sphinx_html/_static/gallery/area/fill_betweenx.json b/src/scitex/_sphinx_html/_static/gallery/area/fill_betweenx.json new file mode 100644 index 000000000..322ba9cdc --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/area/fill_betweenx.json @@ -0,0 +1,117 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:33.445806", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "fill_betweenx", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Value", + "unit": "", + "scale": "linear", + "lim": [ + -1.1493384025373632, + 2.149646185811039 + ], + "n_ticks": 4 + }, + "y": { + "label": "Depth", + "unit": "m", + "scale": "linear", + "lim": [ + -0.5, + 10.5 + ], + "n_ticks": 4 + } + }, + "title": "ax.fill_betweenx()" +} diff --git a/src/scitex/_sphinx_html/_static/gallery/area/stx_fill_between.json b/src/scitex/_sphinx_html/_static/gallery/area/stx_fill_between.json new file mode 100644 index 000000000..cdc871d9e --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/area/stx_fill_between.json @@ -0,0 +1,131 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:33.907583", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_fill_between", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Time", + "unit": "s", + "scale": "linear", + "lim": [ + -0.25, + 5.25 + ], + "n_ticks": 4 + }, + "y": { + "label": "Estimate", + "unit": "", + "scale": "linear", + "lim": [ + -1.5082978735458583, + 1.5080580478162968 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_fill_between()", + "plot_type": "line", + "method": "plot", + "traces": [ + { + "id": "central_estimate", + "color": "#000000", + "linestyle": "-", + "linewidth": 1.0, + "csv_columns": { + "x": "ax_00_plot_0_plot_x", + "y": "ax_00_plot_0_plot_y" + } + } + ] +} diff --git a/src/scitex/_sphinx_html/_static/gallery/area/stx_fillv.json b/src/scitex/_sphinx_html/_static/gallery/area/stx_fillv.json new file mode 100644 index 000000000..7878438d4 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/area/stx_fillv.json @@ -0,0 +1,131 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:34.374599", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_fillv", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Time", + "unit": "s", + "scale": "linear", + "lim": [ + -0.5, + 10.5 + ], + "n_ticks": 4 + }, + "y": { + "label": "Amplitude", + "unit": "", + "scale": "linear", + "lim": [ + -1.0977187159111, + 1.099881918590421 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_fillv()", + "plot_type": "line", + "method": "plot", + "traces": [ + { + "id": "signal", + "color": "#0000ff", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_0_plot_x", + "y": "ax_00_plot_0_plot_y" + } + } + ] +} diff --git a/src/scitex/_sphinx_html/_static/gallery/categorical/bar.json b/src/scitex/_sphinx_html/_static/gallery/categorical/bar.json new file mode 100644 index 000000000..7ef4100af --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/categorical/bar.json @@ -0,0 +1,119 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:23.997014", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "bar", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Category", + "unit": "", + "scale": "linear", + "lim": [ + -0.5900000000000001, + 3.5900000000000003 + ], + "n_ticks": 4 + }, + "y": { + "label": "Count", + "unit": "", + "scale": "linear", + "lim": [ + 0.0, + 81.9 + ], + "n_ticks": 4 + } + }, + "title": "ax.bar()", + "plot_type": "bar", + "method": "bar" +} diff --git a/src/scitex/_sphinx_html/_static/gallery/categorical/barh.json b/src/scitex/_sphinx_html/_static/gallery/categorical/barh.json new file mode 100644 index 000000000..6daec4513 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/categorical/barh.json @@ -0,0 +1,119 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:24.479088", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "barh", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Accuracy", + "unit": "", + "scale": "linear", + "lim": [ + 0.0, + 0.9660000000000001 + ], + "n_ticks": 4 + }, + "y": { + "label": "Method", + "unit": "", + "scale": "linear", + "lim": [ + -0.5900000000000001, + 3.5900000000000003 + ], + "n_ticks": 4 + } + }, + "title": "ax.barh()", + "plot_type": "bar", + "method": "bar" +} diff --git a/src/scitex/_sphinx_html/_static/gallery/categorical/boxplot.json b/src/scitex/_sphinx_html/_static/gallery/categorical/boxplot.json new file mode 100644 index 000000000..32e036dd2 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/categorical/boxplot.json @@ -0,0 +1,307 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:26.010324", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "boxplot", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Group", + "unit": "", + "scale": "linear", + "lim": [ + 0.5, + 3.5 + ], + "n_ticks": 4 + }, + "y": { + "label": "Response", + "unit": "", + "scale": "linear", + "lim": [ + 53.42070915830136, + 145.4772618761839 + ], + "n_ticks": 4 + } + }, + "title": "ax.boxplot()", + "plot_type": "line", + "method": "plot", + "traces": [ + { + "id": "line_0", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_0_plot_x", + "y": "ax_00_plot_0_plot_y" + } + }, + { + "id": "line_1", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_1_plot_x", + "y": "ax_00_plot_1_plot_y" + } + }, + { + "id": "line_2", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_2_plot_x", + "y": "ax_00_plot_2_plot_y" + } + }, + { + "id": "line_3", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_3_plot_x", + "y": "ax_00_plot_3_plot_y" + } + }, + { + "id": "line_4", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_4_plot_x", + "y": "ax_00_plot_4_plot_y" + } + }, + { + "id": "line_5", + "color": "#000000", + "linestyle": "None", + "linewidth": 1.0, + "marker": "o", + "markersize": 2.267716535433071, + "csv_columns": { + "x": "ax_00_plot_5_plot_x", + "y": "ax_00_plot_5_plot_y" + } + }, + { + "id": "line_6", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_6_plot_x", + "y": "ax_00_plot_6_plot_y" + } + }, + { + "id": "line_7", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_7_plot_x", + "y": "ax_00_plot_7_plot_y" + } + }, + { + "id": "line_8", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_8_plot_x", + "y": "ax_00_plot_8_plot_y" + } + }, + { + "id": "line_9", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_9_plot_x", + "y": "ax_00_plot_9_plot_y" + } + }, + { + "id": "line_10", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_10_plot_x", + "y": "ax_00_plot_10_plot_y" + } + }, + { + "id": "line_11", + "color": "#000000", + "linestyle": "None", + "linewidth": 1.0, + "marker": "o", + "markersize": 2.267716535433071, + "csv_columns": { + "x": "ax_00_plot_11_plot_x", + "y": "ax_00_plot_11_plot_y" + } + }, + { + "id": "line_12", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_12_plot_x", + "y": "ax_00_plot_12_plot_y" + } + }, + { + "id": "line_13", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_13_plot_x", + "y": "ax_00_plot_13_plot_y" + } + }, + { + "id": "line_14", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_14_plot_x", + "y": "ax_00_plot_14_plot_y" + } + }, + { + "id": "line_15", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_15_plot_x", + "y": "ax_00_plot_15_plot_y" + } + }, + { + "id": "line_16", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_16_plot_x", + "y": "ax_00_plot_16_plot_y" + } + }, + { + "id": "line_17", + "color": "#000000", + "linestyle": "None", + "linewidth": 1.0, + "marker": "o", + "markersize": 2.267716535433071, + "csv_columns": { + "x": "ax_00_plot_17_plot_x", + "y": "ax_00_plot_17_plot_y" + } + } + ] +} diff --git a/src/scitex/_sphinx_html/_static/gallery/categorical/stx_bar.json b/src/scitex/_sphinx_html/_static/gallery/categorical/stx_bar.json new file mode 100644 index 000000000..6bb67e2d8 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/categorical/stx_bar.json @@ -0,0 +1,119 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:25.038944", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_bar", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Condition", + "unit": "", + "scale": "linear", + "lim": [ + -0.5900000000000001, + 3.5900000000000003 + ], + "n_ticks": 4 + }, + "y": { + "label": "Performance", + "unit": "", + "scale": "linear", + "lim": [ + 0.0, + 0.9450000000000001 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_bar()", + "plot_type": "bar", + "method": "bar" +} diff --git a/src/scitex/_sphinx_html/_static/gallery/categorical/stx_barh.json b/src/scitex/_sphinx_html/_static/gallery/categorical/stx_barh.json new file mode 100644 index 000000000..2b0e7d5f7 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/categorical/stx_barh.json @@ -0,0 +1,119 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:25.514748", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_barh", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Accuracy", + "unit": "", + "scale": "linear", + "lim": [ + 0.0, + 0.9555 + ], + "n_ticks": 4 + }, + "y": { + "label": "Method", + "unit": "", + "scale": "linear", + "lim": [ + -0.5900000000000001, + 3.5900000000000003 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_barh()", + "plot_type": "bar", + "method": "bar" +} diff --git a/src/scitex/_sphinx_html/_static/gallery/categorical/stx_box.json b/src/scitex/_sphinx_html/_static/gallery/categorical/stx_box.json new file mode 100644 index 000000000..b578f63e9 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/categorical/stx_box.json @@ -0,0 +1,369 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:27.472056", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_box", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Condition", + "unit": "", + "scale": "linear", + "lim": [ + 0.5, + 4.5 + ], + "n_ticks": 4 + }, + "y": { + "label": "Score", + "unit": "", + "scale": "linear", + "lim": [ + 18.91105535081504, + 82.52391473314039 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_box()", + "plot_type": "line", + "method": "plot", + "traces": [ + { + "id": "line_0", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_0_plot_x", + "y": "ax_00_plot_0_plot_y" + } + }, + { + "id": "line_1", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_1_plot_x", + "y": "ax_00_plot_1_plot_y" + } + }, + { + "id": "line_2", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_2_plot_x", + "y": "ax_00_plot_2_plot_y" + } + }, + { + "id": "line_3", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_3_plot_x", + "y": "ax_00_plot_3_plot_y" + } + }, + { + "id": "line_4", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_4_plot_x", + "y": "ax_00_plot_4_plot_y" + } + }, + { + "id": "line_5", + "color": "#000000", + "linestyle": "None", + "linewidth": 1.0, + "marker": "o", + "markersize": 2.267716535433071, + "csv_columns": { + "x": "ax_00_plot_5_plot_x", + "y": "ax_00_plot_5_plot_y" + } + }, + { + "id": "line_6", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_6_plot_x", + "y": "ax_00_plot_6_plot_y" + } + }, + { + "id": "line_7", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_7_plot_x", + "y": "ax_00_plot_7_plot_y" + } + }, + { + "id": "line_8", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_8_plot_x", + "y": "ax_00_plot_8_plot_y" + } + }, + { + "id": "line_9", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_9_plot_x", + "y": "ax_00_plot_9_plot_y" + } + }, + { + "id": "line_10", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_10_plot_x", + "y": "ax_00_plot_10_plot_y" + } + }, + { + "id": "line_11", + "color": "#000000", + "linestyle": "None", + "linewidth": 1.0, + "marker": "o", + "markersize": 2.267716535433071, + "csv_columns": { + "x": "ax_00_plot_11_plot_x", + "y": "ax_00_plot_11_plot_y" + } + }, + { + "id": "line_12", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_12_plot_x", + "y": "ax_00_plot_12_plot_y" + } + }, + { + "id": "line_13", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_13_plot_x", + "y": "ax_00_plot_13_plot_y" + } + }, + { + "id": "line_14", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_14_plot_x", + "y": "ax_00_plot_14_plot_y" + } + }, + { + "id": "line_15", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_15_plot_x", + "y": "ax_00_plot_15_plot_y" + } + }, + { + "id": "line_16", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_16_plot_x", + "y": "ax_00_plot_16_plot_y" + } + }, + { + "id": "line_17", + "color": "#000000", + "linestyle": "None", + "linewidth": 1.0, + "marker": "o", + "markersize": 2.267716535433071, + "csv_columns": { + "x": "ax_00_plot_17_plot_x", + "y": "ax_00_plot_17_plot_y" + } + }, + { + "id": "line_18", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_18_plot_x", + "y": "ax_00_plot_18_plot_y" + } + }, + { + "id": "line_19", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_19_plot_x", + "y": "ax_00_plot_19_plot_y" + } + }, + { + "id": "line_20", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_20_plot_x", + "y": "ax_00_plot_20_plot_y" + } + }, + { + "id": "line_21", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_21_plot_x", + "y": "ax_00_plot_21_plot_y" + } + }, + { + "id": "line_22", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_22_plot_x", + "y": "ax_00_plot_22_plot_y" + } + }, + { + "id": "line_23", + "color": "#000000", + "linestyle": "None", + "linewidth": 1.0, + "marker": "o", + "markersize": 2.267716535433071, + "csv_columns": { + "x": "ax_00_plot_23_plot_x", + "y": "ax_00_plot_23_plot_y" + } + } + ] +} diff --git a/src/scitex/_sphinx_html/_static/gallery/categorical/stx_boxplot.json b/src/scitex/_sphinx_html/_static/gallery/categorical/stx_boxplot.json new file mode 100644 index 000000000..94e1694f8 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/categorical/stx_boxplot.json @@ -0,0 +1,369 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:28.879691", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_boxplot", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Group", + "unit": "", + "scale": "linear", + "lim": [ + 0.5, + 4.5 + ], + "n_ticks": 4 + }, + "y": { + "label": "Value", + "unit": "", + "scale": "linear", + "lim": [ + -0.45524268338989954, + 9.68191959945847 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_boxplot()", + "plot_type": "line", + "method": "plot", + "traces": [ + { + "id": "line_0", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_0_plot_x", + "y": "ax_00_plot_0_plot_y" + } + }, + { + "id": "line_1", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_1_plot_x", + "y": "ax_00_plot_1_plot_y" + } + }, + { + "id": "line_2", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_2_plot_x", + "y": "ax_00_plot_2_plot_y" + } + }, + { + "id": "line_3", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_3_plot_x", + "y": "ax_00_plot_3_plot_y" + } + }, + { + "id": "line_4", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_4_plot_x", + "y": "ax_00_plot_4_plot_y" + } + }, + { + "id": "line_5", + "color": "#000000", + "linestyle": "None", + "linewidth": 1.0, + "marker": "o", + "markersize": 2.267716535433071, + "csv_columns": { + "x": "ax_00_plot_5_plot_x", + "y": "ax_00_plot_5_plot_y" + } + }, + { + "id": "line_6", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_6_plot_x", + "y": "ax_00_plot_6_plot_y" + } + }, + { + "id": "line_7", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_7_plot_x", + "y": "ax_00_plot_7_plot_y" + } + }, + { + "id": "line_8", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_8_plot_x", + "y": "ax_00_plot_8_plot_y" + } + }, + { + "id": "line_9", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_9_plot_x", + "y": "ax_00_plot_9_plot_y" + } + }, + { + "id": "line_10", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_10_plot_x", + "y": "ax_00_plot_10_plot_y" + } + }, + { + "id": "line_11", + "color": "#000000", + "linestyle": "None", + "linewidth": 1.0, + "marker": "o", + "markersize": 2.267716535433071, + "csv_columns": { + "x": "ax_00_plot_11_plot_x", + "y": "ax_00_plot_11_plot_y" + } + }, + { + "id": "line_12", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_12_plot_x", + "y": "ax_00_plot_12_plot_y" + } + }, + { + "id": "line_13", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_13_plot_x", + "y": "ax_00_plot_13_plot_y" + } + }, + { + "id": "line_14", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_14_plot_x", + "y": "ax_00_plot_14_plot_y" + } + }, + { + "id": "line_15", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_15_plot_x", + "y": "ax_00_plot_15_plot_y" + } + }, + { + "id": "line_16", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_16_plot_x", + "y": "ax_00_plot_16_plot_y" + } + }, + { + "id": "line_17", + "color": "#000000", + "linestyle": "None", + "linewidth": 1.0, + "marker": "o", + "markersize": 2.267716535433071, + "csv_columns": { + "x": "ax_00_plot_17_plot_x", + "y": "ax_00_plot_17_plot_y" + } + }, + { + "id": "line_18", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_18_plot_x", + "y": "ax_00_plot_18_plot_y" + } + }, + { + "id": "line_19", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_19_plot_x", + "y": "ax_00_plot_19_plot_y" + } + }, + { + "id": "line_20", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_20_plot_x", + "y": "ax_00_plot_20_plot_y" + } + }, + { + "id": "line_21", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_21_plot_x", + "y": "ax_00_plot_21_plot_y" + } + }, + { + "id": "line_22", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_22_plot_x", + "y": "ax_00_plot_22_plot_y" + } + }, + { + "id": "line_23", + "color": "#000000", + "linestyle": "None", + "linewidth": 1.0, + "marker": "o", + "markersize": 2.267716535433071, + "csv_columns": { + "x": "ax_00_plot_23_plot_x", + "y": "ax_00_plot_23_plot_y" + } + } + ] +} diff --git a/src/scitex/_sphinx_html/_static/gallery/categorical/stx_violin.json b/src/scitex/_sphinx_html/_static/gallery/categorical/stx_violin.json new file mode 100644 index 000000000..9f8a47b10 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/categorical/stx_violin.json @@ -0,0 +1,217 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:28.271003", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_violin", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Condition", + "unit": "", + "scale": "linear", + "lim": [ + -0.5, + 2.5 + ], + "n_ticks": 4 + }, + "y": { + "label": "Effect Size", + "unit": "", + "scale": "linear", + "lim": [ + -3.7486260449978, + 5.178626671530489 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_violin()", + "plot_type": "line", + "method": "plot", + "traces": [ + { + "id": "line_0", + "color": "#393939", + "linestyle": "-", + "linewidth": 1.875, + "csv_columns": { + "x": "ax_00_plot_0_plot_x", + "y": "ax_00_plot_0_plot_y" + } + }, + { + "id": "line_1", + "color": "#393939", + "linestyle": "-", + "linewidth": 5.625, + "csv_columns": { + "x": "ax_00_plot_1_plot_x", + "y": "ax_00_plot_1_plot_y" + } + }, + { + "id": "line_2", + "color": "#393939", + "linestyle": "-", + "linewidth": 0.56693, + "marker": "_", + "markersize": 4.6875, + "csv_columns": { + "x": "ax_00_plot_2_plot_x", + "y": "ax_00_plot_2_plot_y" + } + }, + { + "id": "line_3", + "color": "#393939", + "linestyle": "-", + "linewidth": 1.875, + "csv_columns": { + "x": "ax_00_plot_3_plot_x", + "y": "ax_00_plot_3_plot_y" + } + }, + { + "id": "line_4", + "color": "#393939", + "linestyle": "-", + "linewidth": 5.625, + "csv_columns": { + "x": "ax_00_plot_4_plot_x", + "y": "ax_00_plot_4_plot_y" + } + }, + { + "id": "line_5", + "color": "#393939", + "linestyle": "-", + "linewidth": 0.56693, + "marker": "_", + "markersize": 4.6875, + "csv_columns": { + "x": "ax_00_plot_5_plot_x", + "y": "ax_00_plot_5_plot_y" + } + }, + { + "id": "line_6", + "color": "#393939", + "linestyle": "-", + "linewidth": 1.875, + "csv_columns": { + "x": "ax_00_plot_6_plot_x", + "y": "ax_00_plot_6_plot_y" + } + }, + { + "id": "line_7", + "color": "#393939", + "linestyle": "-", + "linewidth": 5.625, + "csv_columns": { + "x": "ax_00_plot_7_plot_x", + "y": "ax_00_plot_7_plot_y" + } + }, + { + "id": "line_8", + "color": "#393939", + "linestyle": "-", + "linewidth": 0.56693, + "marker": "_", + "markersize": 4.6875, + "csv_columns": { + "x": "ax_00_plot_8_plot_x", + "y": "ax_00_plot_8_plot_y" + } + } + ] +} diff --git a/src/scitex/_sphinx_html/_static/gallery/categorical/stx_violinplot.json b/src/scitex/_sphinx_html/_static/gallery/categorical/stx_violinplot.json new file mode 100644 index 000000000..533ed473d --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/categorical/stx_violinplot.json @@ -0,0 +1,217 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:29.676291", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_violinplot", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Condition", + "unit": "", + "scale": "linear", + "lim": [ + -0.5, + 2.5 + ], + "n_ticks": 4 + }, + "y": { + "label": "Value", + "unit": "", + "scale": "linear", + "lim": [ + -3.209678546837379, + 3.5762504320542554 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_violinplot()", + "plot_type": "line", + "method": "plot", + "traces": [ + { + "id": "line_0", + "color": "#393939", + "linestyle": "-", + "linewidth": 1.875, + "csv_columns": { + "x": "ax_00_plot_0_plot_x", + "y": "ax_00_plot_0_plot_y" + } + }, + { + "id": "line_1", + "color": "#393939", + "linestyle": "-", + "linewidth": 5.625, + "csv_columns": { + "x": "ax_00_plot_1_plot_x", + "y": "ax_00_plot_1_plot_y" + } + }, + { + "id": "line_2", + "color": "#393939", + "linestyle": "-", + "linewidth": 0.56693, + "marker": "_", + "markersize": 4.6875, + "csv_columns": { + "x": "ax_00_plot_2_plot_x", + "y": "ax_00_plot_2_plot_y" + } + }, + { + "id": "line_3", + "color": "#393939", + "linestyle": "-", + "linewidth": 1.875, + "csv_columns": { + "x": "ax_00_plot_3_plot_x", + "y": "ax_00_plot_3_plot_y" + } + }, + { + "id": "line_4", + "color": "#393939", + "linestyle": "-", + "linewidth": 5.625, + "csv_columns": { + "x": "ax_00_plot_4_plot_x", + "y": "ax_00_plot_4_plot_y" + } + }, + { + "id": "line_5", + "color": "#393939", + "linestyle": "-", + "linewidth": 0.56693, + "marker": "_", + "markersize": 4.6875, + "csv_columns": { + "x": "ax_00_plot_5_plot_x", + "y": "ax_00_plot_5_plot_y" + } + }, + { + "id": "line_6", + "color": "#393939", + "linestyle": "-", + "linewidth": 1.875, + "csv_columns": { + "x": "ax_00_plot_6_plot_x", + "y": "ax_00_plot_6_plot_y" + } + }, + { + "id": "line_7", + "color": "#393939", + "linestyle": "-", + "linewidth": 5.625, + "csv_columns": { + "x": "ax_00_plot_7_plot_x", + "y": "ax_00_plot_7_plot_y" + } + }, + { + "id": "line_8", + "color": "#393939", + "linestyle": "-", + "linewidth": 0.56693, + "marker": "_", + "markersize": 4.6875, + "csv_columns": { + "x": "ax_00_plot_8_plot_x", + "y": "ax_00_plot_8_plot_y" + } + } + ] +} diff --git a/src/scitex/_sphinx_html/_static/gallery/categorical/violinplot.json b/src/scitex/_sphinx_html/_static/gallery/categorical/violinplot.json new file mode 100644 index 000000000..f3128b1f9 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/categorical/violinplot.json @@ -0,0 +1,369 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:26.702221", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "violinplot", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Condition", + "unit": "", + "scale": "linear", + "lim": [ + 0.575, + 4.425 + ], + "n_ticks": 4 + }, + "y": { + "label": "Value", + "unit": "", + "scale": "linear", + "lim": [ + -5.864181138112348, + 6.333539063319912 + ], + "n_ticks": 4 + } + }, + "title": "ax.violinplot()", + "plot_type": "line", + "method": "plot", + "traces": [ + { + "id": "line_0", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_0_plot_x", + "y": "ax_00_plot_0_plot_y" + } + }, + { + "id": "line_1", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_1_plot_x", + "y": "ax_00_plot_1_plot_y" + } + }, + { + "id": "line_2", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_2_plot_x", + "y": "ax_00_plot_2_plot_y" + } + }, + { + "id": "line_3", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_3_plot_x", + "y": "ax_00_plot_3_plot_y" + } + }, + { + "id": "line_4", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_4_plot_x", + "y": "ax_00_plot_4_plot_y" + } + }, + { + "id": "line_5", + "color": "#000000", + "linestyle": "None", + "linewidth": 1.0, + "marker": "o", + "markersize": 2.267716535433071, + "csv_columns": { + "x": "ax_00_plot_5_plot_x", + "y": "ax_00_plot_5_plot_y" + } + }, + { + "id": "line_6", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_6_plot_x", + "y": "ax_00_plot_6_plot_y" + } + }, + { + "id": "line_7", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_7_plot_x", + "y": "ax_00_plot_7_plot_y" + } + }, + { + "id": "line_8", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_8_plot_x", + "y": "ax_00_plot_8_plot_y" + } + }, + { + "id": "line_9", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_9_plot_x", + "y": "ax_00_plot_9_plot_y" + } + }, + { + "id": "line_10", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_10_plot_x", + "y": "ax_00_plot_10_plot_y" + } + }, + { + "id": "line_11", + "color": "#000000", + "linestyle": "None", + "linewidth": 1.0, + "marker": "o", + "markersize": 2.267716535433071, + "csv_columns": { + "x": "ax_00_plot_11_plot_x", + "y": "ax_00_plot_11_plot_y" + } + }, + { + "id": "line_12", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_12_plot_x", + "y": "ax_00_plot_12_plot_y" + } + }, + { + "id": "line_13", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_13_plot_x", + "y": "ax_00_plot_13_plot_y" + } + }, + { + "id": "line_14", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_14_plot_x", + "y": "ax_00_plot_14_plot_y" + } + }, + { + "id": "line_15", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_15_plot_x", + "y": "ax_00_plot_15_plot_y" + } + }, + { + "id": "line_16", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_16_plot_x", + "y": "ax_00_plot_16_plot_y" + } + }, + { + "id": "line_17", + "color": "#000000", + "linestyle": "None", + "linewidth": 1.0, + "marker": "o", + "markersize": 2.267716535433071, + "csv_columns": { + "x": "ax_00_plot_17_plot_x", + "y": "ax_00_plot_17_plot_y" + } + }, + { + "id": "line_18", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_18_plot_x", + "y": "ax_00_plot_18_plot_y" + } + }, + { + "id": "line_19", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_19_plot_x", + "y": "ax_00_plot_19_plot_y" + } + }, + { + "id": "line_20", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_20_plot_x", + "y": "ax_00_plot_20_plot_y" + } + }, + { + "id": "line_21", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_21_plot_x", + "y": "ax_00_plot_21_plot_y" + } + }, + { + "id": "line_22", + "color": "#000000", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_22_plot_x", + "y": "ax_00_plot_22_plot_y" + } + }, + { + "id": "line_23", + "color": "#000000", + "linestyle": "None", + "linewidth": 1.0, + "marker": "o", + "markersize": 2.267716535433071, + "csv_columns": { + "x": "ax_00_plot_23_plot_x", + "y": "ax_00_plot_23_plot_y" + } + } + ] +} diff --git a/src/scitex/_sphinx_html/_static/gallery/contour/contour.json b/src/scitex/_sphinx_html/_static/gallery/contour/contour.json new file mode 100644 index 000000000..188a36a07 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/contour/contour.json @@ -0,0 +1,119 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:39.399305", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "contour", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "X", + "unit": "", + "scale": "linear", + "lim": [ + -3.0, + 3.0 + ], + "n_ticks": 4 + }, + "y": { + "label": "Y", + "unit": "", + "scale": "linear", + "lim": [ + -3.0, + 3.0 + ], + "n_ticks": 4 + } + }, + "title": "ax.contour()", + "plot_type": "contour", + "method": "contour" +} diff --git a/src/scitex/_sphinx_html/_static/gallery/contour/contourf.json b/src/scitex/_sphinx_html/_static/gallery/contour/contourf.json new file mode 100644 index 000000000..4e548b64a --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/contour/contourf.json @@ -0,0 +1,119 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:40.272395", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "contourf", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 31.999999999999993, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.2598425196850391, + 1.1023622047244093 + ], + "axes_size_px": [ + 377, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.3999999999999999, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 28.000000000000007, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 613, + "y1": 566, + "width": 377, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 51.99999999999999, + "y1": 47.99999999999999, + "width": 31.999999999999993, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "X", + "unit": "", + "scale": "linear", + "lim": [ + -3.0, + 3.0 + ], + "n_ticks": 4 + }, + "y": { + "label": "Y", + "unit": "", + "scale": "linear", + "lim": [ + -3.0, + 3.0 + ], + "n_ticks": 4 + } + }, + "title": "ax.contourf()", + "plot_type": "contour", + "method": "contour" +} diff --git a/src/scitex/_sphinx_html/_static/gallery/contour/stx_contour.json b/src/scitex/_sphinx_html/_static/gallery/contour/stx_contour.json new file mode 100644 index 000000000..5b998ea17 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/contour/stx_contour.json @@ -0,0 +1,119 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:41.124194", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_contour", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "X", + "unit": "", + "scale": "linear", + "lim": [ + -2.0, + 2.0 + ], + "n_ticks": 4 + }, + "y": { + "label": "Y", + "unit": "", + "scale": "linear", + "lim": [ + -2.0, + 2.0 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_contour()", + "plot_type": "contour", + "method": "contour" +} diff --git a/src/scitex/_sphinx_html/_static/gallery/distribution/hist.json b/src/scitex/_sphinx_html/_static/gallery/distribution/hist.json new file mode 100644 index 000000000..460b6a927 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/distribution/hist.json @@ -0,0 +1,119 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:21.313658", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "hist", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Reaction Time", + "unit": "ms", + "scale": "linear", + "lim": [ + 162.322617471579, + 786.5945145752729 + ], + "n_ticks": 4 + }, + "y": { + "label": "Count", + "unit": "", + "scale": "linear", + "lim": [ + 0.0, + 107.1 + ], + "n_ticks": 4 + } + }, + "title": "ax.hist()", + "plot_type": "bar", + "method": "bar" +} diff --git a/src/scitex/_sphinx_html/_static/gallery/distribution/hist2d.json b/src/scitex/_sphinx_html/_static/gallery/distribution/hist2d.json new file mode 100644 index 000000000..0376d944b --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/distribution/hist2d.json @@ -0,0 +1,117 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:21.804784", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "hist2d", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "X Position", + "unit": "cm", + "scale": "linear", + "lim": [ + -3.2412673400690726, + 3.9262377064363267 + ], + "n_ticks": 4 + }, + "y": { + "label": "Y Position", + "unit": "cm", + "scale": "linear", + "lim": [ + -3.9224002516183423, + 3.529055187570484 + ], + "n_ticks": 4 + } + }, + "title": "ax.hist2d()" +} diff --git a/src/scitex/_sphinx_html/_static/gallery/distribution/stx_ecdf.json b/src/scitex/_sphinx_html/_static/gallery/distribution/stx_ecdf.json new file mode 100644 index 000000000..4c44c08be --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/distribution/stx_ecdf.json @@ -0,0 +1,131 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:22.919719", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_ecdf", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Latency", + "unit": "ms", + "scale": "linear", + "lim": [ + -12.12566357459574, + 260.2208158234872 + ], + "n_ticks": 4 + }, + "y": { + "label": "Cumulative Probability", + "unit": "", + "scale": "linear", + "lim": [ + 0.0, + 100.0 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_ecdf()", + "plot_type": "line", + "method": "plot", + "traces": [ + { + "id": "line_0", + "color": "#0080bf", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_0_plot_x", + "y": "ax_00_plot_0_plot_y" + } + } + ] +} diff --git a/src/scitex/_sphinx_html/_static/gallery/distribution/stx_joyplot.json b/src/scitex/_sphinx_html/_static/gallery/distribution/stx_joyplot.json new file mode 100644 index 000000000..6d5d1549d --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/distribution/stx_joyplot.json @@ -0,0 +1,171 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:23.574947", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_joyplot", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Value", + "unit": "", + "scale": "linear", + "lim": [ + -3.074570807107107, + 5.000320664534208 + ], + "n_ticks": 4 + }, + "y": { + "label": "Group", + "unit": "", + "scale": "linear", + "lim": [ + -0.1, + 3.833333333333333 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_joyplot()", + "plot_type": "line", + "method": "plot", + "traces": [ + { + "id": "line_0", + "color": "#c832ff", + "linestyle": "-", + "linewidth": 1.0, + "csv_columns": { + "x": "ax_00_plot_0_plot_x", + "y": "ax_00_plot_0_plot_y" + } + }, + { + "id": "line_1", + "color": "#e6a014", + "linestyle": "-", + "linewidth": 1.0, + "csv_columns": { + "x": "ax_00_plot_1_plot_x", + "y": "ax_00_plot_1_plot_y" + } + }, + { + "id": "line_2", + "color": "#14b414", + "linestyle": "-", + "linewidth": 1.0, + "csv_columns": { + "x": "ax_00_plot_2_plot_x", + "y": "ax_00_plot_2_plot_y" + } + }, + { + "id": "line_3", + "color": "#ff4632", + "linestyle": "-", + "linewidth": 1.0, + "csv_columns": { + "x": "ax_00_plot_3_plot_x", + "y": "ax_00_plot_3_plot_y" + } + }, + { + "id": "line_4", + "color": "#0080c0", + "linestyle": "-", + "linewidth": 1.0, + "csv_columns": { + "x": "ax_00_plot_4_plot_x", + "y": "ax_00_plot_4_plot_y" + } + } + ] +} diff --git a/src/scitex/_sphinx_html/_static/gallery/distribution/stx_kde.json b/src/scitex/_sphinx_html/_static/gallery/distribution/stx_kde.json new file mode 100644 index 000000000..99b80c8cb --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/distribution/stx_kde.json @@ -0,0 +1,131 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:22.383160", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_kde", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Score", + "unit": "", + "scale": "linear", + "lim": [ + 34.040327183947376, + 112.07431432190911 + ], + "n_ticks": 4 + }, + "y": { + "label": "Density", + "unit": "", + "scale": "linear", + "lim": [ + -9.763529695187793e-05, + 0.0023418545764972956 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_kde()", + "plot_type": "line", + "method": "plot", + "traces": [ + { + "id": "line_0", + "color": "#000000", + "linestyle": "--", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_0_plot_x", + "y": "ax_00_plot_0_plot_y" + } + } + ] +} diff --git a/src/scitex/_sphinx_html/_static/gallery/grid/imshow.json b/src/scitex/_sphinx_html/_static/gallery/grid/imshow.json new file mode 100644 index 000000000..4b9ef7fe8 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/grid/imshow.json @@ -0,0 +1,119 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:35.036675", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "imshow", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 28.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.1023622047244095, + 1.1023622047244093 + ], + "axes_size_px": [ + 330, + 330 + ], + "axes_position": [ + 0.29999999999999993, + 0.29411764705882354, + 0.35, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 23.999999999999993, + "bottom": 20.0, + "right": 28.000000000000007, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 283, + "y0": 236, + "x1": 613, + "y1": 566, + "width": 330, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 23.999999999999993, + "y0": 20.0, + "x1": 51.99999999999999, + "y1": 47.99999999999999, + "width": 28.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "X", + "unit": "", + "scale": "linear", + "lim": [ + -0.5, + 19.5 + ], + "n_ticks": 4 + }, + "y": { + "label": "Y", + "unit": "", + "scale": "linear", + "lim": [ + 19.5, + -0.5 + ], + "n_ticks": 4 + } + }, + "title": "ax.imshow()", + "plot_type": "image", + "method": "imshow" +} diff --git a/src/scitex/_sphinx_html/_static/gallery/grid/matshow.json b/src/scitex/_sphinx_html/_static/gallery/grid/matshow.json new file mode 100644 index 000000000..a2cd55477 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/grid/matshow.json @@ -0,0 +1,119 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:35.714685", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "matshow", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 28.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.1023622047244095, + 1.1023622047244093 + ], + "axes_size_px": [ + 330, + 330 + ], + "axes_position": [ + 0.29999999999999993, + 0.29411764705882354, + 0.35, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 23.999999999999993, + "bottom": 20.0, + "right": 28.000000000000007, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 283, + "y0": 236, + "x1": 613, + "y1": 566, + "width": 330, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 23.999999999999993, + "y0": 20.0, + "x1": 51.99999999999999, + "y1": 47.99999999999999, + "width": 28.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Variable", + "unit": "", + "scale": "linear", + "lim": [ + -0.5, + 4.5 + ], + "n_ticks": 4 + }, + "y": { + "label": "Variable", + "unit": "", + "scale": "linear", + "lim": [ + 4.5, + -0.5 + ], + "n_ticks": 4 + } + }, + "title": "ax.matshow()", + "plot_type": "image", + "method": "imshow" +} diff --git a/src/scitex/_sphinx_html/_static/gallery/grid/stx_conf_mat.json b/src/scitex/_sphinx_html/_static/gallery/grid/stx_conf_mat.json new file mode 100644 index 000000000..f1709734f --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/grid/stx_conf_mat.json @@ -0,0 +1,117 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:38.712362", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_conf_mat", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 27.99999999999999, + 27.99999999999999 + ], + "axes_size_inch": [ + 1.102362204724409, + 1.102362204724409 + ], + "axes_size_px": [ + 330, + 330 + ], + "axes_position": [ + 0.32500000000000007, + 0.2941176470588236, + 0.34999999999999987, + 0.4117647058823528 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 26.000000000000007, + "bottom": 20.000000000000004, + "right": 26.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 306, + "y0": 236, + "x1": 636, + "y1": 566, + "width": 330, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 26.000000000000007, + "y0": 20.000000000000004, + "x1": 54.0, + "y1": 47.99999999999999, + "width": 27.99999999999999, + "height": 27.99999999999999 + }, + "axes": { + "x": { + "label": "Predicted", + "unit": "", + "scale": "linear", + "lim": [ + 0.0, + 3.0 + ], + "n_ticks": 4 + }, + "y": { + "label": "Actual", + "unit": "", + "scale": "linear", + "lim": [ + 0.0, + 3.0 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_conf_mat()" +} diff --git a/src/scitex/_sphinx_html/_static/gallery/grid/stx_heatmap.json b/src/scitex/_sphinx_html/_static/gallery/grid/stx_heatmap.json new file mode 100644 index 000000000..383a57cac --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/grid/stx_heatmap.json @@ -0,0 +1,119 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:37.747073", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_heatmap", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 28.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.1023622047244095, + 1.1023622047244093 + ], + "axes_size_px": [ + 330, + 330 + ], + "axes_position": [ + 0.29999999999999993, + 0.29411764705882354, + 0.35, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 23.999999999999993, + "bottom": 20.0, + "right": 28.000000000000007, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 283, + "y0": 236, + "x1": 613, + "y1": 566, + "width": 330, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 23.999999999999993, + "y0": 20.0, + "x1": 51.99999999999999, + "y1": 47.99999999999999, + "width": 28.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Feature", + "unit": "", + "scale": "linear", + "lim": [ + -0.5, + 4.5 + ], + "n_ticks": 4 + }, + "y": { + "label": "Sample", + "unit": "", + "scale": "linear", + "lim": [ + 4.5, + -0.5 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_heatmap()", + "plot_type": "image", + "method": "imshow" +} diff --git a/src/scitex/_sphinx_html/_static/gallery/grid/stx_image.json b/src/scitex/_sphinx_html/_static/gallery/grid/stx_image.json new file mode 100644 index 000000000..d06fb2cfe --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/grid/stx_image.json @@ -0,0 +1,119 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:37.076693", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_image", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 36.56, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.4393700787401578, + 1.1023622047244093 + ], + "axes_size_px": [ + 431, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.4570000000000001, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 23.439999999999998, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 667, + "y1": 566, + "width": 431, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 56.56, + "y1": 47.99999999999999, + "width": 36.56, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "X", + "unit": "", + "scale": "linear", + "lim": [ + -0.5, + 31.5 + ], + "n_ticks": 4 + }, + "y": { + "label": "Y", + "unit": "", + "scale": "linear", + "lim": [ + -0.5, + 31.5 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_image()", + "plot_type": "image", + "method": "imshow" +} diff --git a/src/scitex/_sphinx_html/_static/gallery/grid/stx_imshow.json b/src/scitex/_sphinx_html/_static/gallery/grid/stx_imshow.json new file mode 100644 index 000000000..26e67cd31 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/grid/stx_imshow.json @@ -0,0 +1,119 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:36.302703", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_imshow", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 28.000000000000004, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.1023622047244097, + 1.1023622047244093 + ], + "axes_size_px": [ + 330, + 330 + ], + "axes_position": [ + 0.325, + 0.29411764705882354, + 0.35000000000000003, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 26.0, + "bottom": 20.0, + "right": 26.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 306, + "y0": 236, + "x1": 636, + "y1": 566, + "width": 330, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 26.0, + "y0": 20.0, + "x1": 54.0, + "y1": 47.99999999999999, + "width": 28.000000000000004, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "X", + "unit": "voxels", + "scale": "linear", + "lim": [ + -0.5, + 63.5 + ], + "n_ticks": 4 + }, + "y": { + "label": "Y", + "unit": "voxels", + "scale": "linear", + "lim": [ + 63.5, + -0.5 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_imshow()", + "plot_type": "image", + "method": "imshow" +} diff --git a/src/scitex/_sphinx_html/_static/gallery/line/plot.json b/src/scitex/_sphinx_html/_static/gallery/line/plot.json new file mode 100644 index 000000000..7f279613a --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/line/plot.json @@ -0,0 +1,152 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:15.641348", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "plot", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Time", + "unit": "s", + "scale": "linear", + "lim": [ + -0.3141592653589793, + 6.5973445725385655 + ], + "n_ticks": 4 + }, + "y": { + "label": "Amplitude", + "unit": "a.u.", + "scale": "linear", + "lim": [ + -1.099867834057569, + 1.0999937063836938 + ], + "n_ticks": 4 + } + }, + "title": "ax.plot()", + "plot_type": "line", + "method": "plot", + "traces": [ + { + "id": "sine_wave", + "label": "sin", + "color": "#0000ff", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_0_plot_x", + "y": "ax_00_plot_0_plot_y" + } + }, + { + "id": "cosine_wave", + "label": "cos", + "color": "#ff0000", + "linestyle": "--", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_1_plot_x", + "y": "ax_00_plot_1_plot_y" + } + } + ], + "legend": { + "visible": true, + "loc": 0, + "frameon": false, + "labels": [ + "sin", + "cos" + ] + } +} diff --git a/src/scitex/_sphinx_html/_static/gallery/line/step.json b/src/scitex/_sphinx_html/_static/gallery/line/step.json new file mode 100644 index 000000000..fe33c6c9f --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/line/step.json @@ -0,0 +1,131 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:16.799913", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "step", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Time", + "unit": "ms", + "scale": "linear", + "lim": [ + -0.9500000000000001, + 19.95 + ], + "n_ticks": 4 + }, + "y": { + "label": "Voltage", + "unit": "mV", + "scale": "linear", + "lim": [ + -0.2, + 4.2 + ], + "n_ticks": 4 + } + }, + "title": "ax.step()", + "plot_type": "line", + "method": "plot", + "traces": [ + { + "id": "digital_signal", + "color": "#0080bf", + "linestyle": "-", + "linewidth": 0.56693, + "csv_columns": { + "x": "ax_00_plot_0_plot_x", + "y": "ax_00_plot_0_plot_y" + } + } + ] +} diff --git a/src/scitex/_sphinx_html/_static/gallery/line/stx_line.json b/src/scitex/_sphinx_html/_static/gallery/line/stx_line.json new file mode 100644 index 000000000..8f2012489 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/line/stx_line.json @@ -0,0 +1,140 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:17.384765", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_line", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Sample", + "unit": "", + "scale": "linear", + "lim": [ + -4.95, + 103.95 + ], + "n_ticks": 4 + }, + "y": { + "label": "Amplitude", + "unit": "\u03bcV", + "scale": "linear", + "lim": [ + -1.3085321119130287, + 1.1706692688128397 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_line()", + "plot_type": "line", + "method": "plot", + "traces": [ + { + "id": "Signal", + "label": "Signal", + "color": "#0080bf", + "linestyle": "-", + "linewidth": 0.56693, + "csv_columns": { + "x": "ax_00_plot_0_plot_x", + "y": "ax_00_plot_0_plot_y" + } + } + ], + "legend": { + "visible": true, + "loc": 0, + "frameon": false, + "labels": [ + "Signal" + ] + } +} diff --git a/src/scitex/_sphinx_html/_static/gallery/line/stx_shaded_line.json b/src/scitex/_sphinx_html/_static/gallery/line/stx_shaded_line.json new file mode 100644 index 000000000..f413347c4 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/line/stx_shaded_line.json @@ -0,0 +1,131 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:17.890611", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_shaded_line", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Time", + "unit": "s", + "scale": "linear", + "lim": [ + -0.5, + 10.5 + ], + "n_ticks": 4 + }, + "y": { + "label": "Response", + "unit": "a.u.", + "scale": "linear", + "lim": [ + -1.429338402537363, + 1.429646185811039 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_shaded_line()", + "plot_type": "line", + "method": "plot", + "traces": [ + { + "id": "line_0", + "color": "#0080bf", + "linestyle": "-", + "linewidth": 0.56693, + "csv_columns": { + "x": "ax_00_plot_0_plot_x", + "y": "ax_00_plot_0_plot_y" + } + } + ] +} diff --git a/src/scitex/_sphinx_html/_static/gallery/scatter/hexbin.json b/src/scitex/_sphinx_html/_static/gallery/scatter/hexbin.json new file mode 100644 index 000000000..93f6e74a2 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/scatter/hexbin.json @@ -0,0 +1,117 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:32.333286", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "hexbin", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "X", + "unit": "", + "scale": "linear", + "lim": [ + -8.629664316309155, + 8.637339225945123 + ], + "n_ticks": 4 + }, + "y": { + "label": "Y", + "unit": "", + "scale": "linear", + "lim": [ + -8.54629661650783, + 9.791714460078149 + ], + "n_ticks": 4 + } + }, + "title": "ax.hexbin()" +} diff --git a/src/scitex/_sphinx_html/_static/gallery/scatter/scatter.json b/src/scitex/_sphinx_html/_static/gallery/scatter/scatter.json new file mode 100644 index 000000000..8643c4b6e --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/scatter/scatter.json @@ -0,0 +1,119 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:30.230402", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "scatter", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Age", + "unit": "years", + "scale": "linear", + "lim": [ + 17.3872325689854, + 82.15731065446178 + ], + "n_ticks": 4 + }, + "y": { + "label": "Response Time", + "unit": "ms", + "scale": "linear", + "lim": [ + 199.14344813617072, + 409.08860826337786 + ], + "n_ticks": 4 + } + }, + "title": "ax.scatter()", + "plot_type": "scatter", + "method": "scatter" +} diff --git a/src/scitex/_sphinx_html/_static/gallery/scatter/stem.json b/src/scitex/_sphinx_html/_static/gallery/scatter/stem.json new file mode 100644 index 000000000..e7852aaeb --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/scatter/stem.json @@ -0,0 +1,143 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:31.846975", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stem", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Frequency", + "unit": "Hz", + "scale": "linear", + "lim": [ + -2.25, + 47.25 + ], + "n_ticks": 4 + }, + "y": { + "label": "Power", + "unit": "dB", + "scale": "linear", + "lim": [ + -0.08162137929650234, + 1.7140489652265491 + ], + "n_ticks": 4 + } + }, + "title": "ax.stem()", + "plot_type": "bar", + "method": "bar", + "traces": [ + { + "id": "frequency_spectrum", + "color": "#0080bf", + "linestyle": "None", + "linewidth": 0.56693, + "marker": "o", + "markersize": 2.267716535433071, + "csv_columns": { + "x": "ax_00_plot_0_plot_x", + "y": "ax_00_plot_0_plot_y" + } + }, + { + "id": "frequency_spectrum", + "color": "#000000", + "linestyle": "--", + "linewidth": 0.56693, + "csv_columns": { + "x": "ax_00_plot_1_plot_x", + "y": "ax_00_plot_1_plot_y" + } + } + ] +} diff --git a/src/scitex/_sphinx_html/_static/gallery/scatter/stx_scatter.json b/src/scitex/_sphinx_html/_static/gallery/scatter/stx_scatter.json new file mode 100644 index 000000000..d4817ea62 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/scatter/stx_scatter.json @@ -0,0 +1,119 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:31.319212", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_scatter", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Feature X", + "unit": "", + "scale": "linear", + "lim": [ + -2.873894464918496, + 2.717391473314038 + ], + "n_ticks": 4 + }, + "y": { + "label": "Feature Y", + "unit": "", + "scale": "linear", + "lim": [ + -2.3037392477610648, + 2.843199076316539 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_scatter()", + "plot_type": "scatter", + "method": "scatter" +} diff --git a/src/scitex/_sphinx_html/_static/gallery/special/pie.json b/src/scitex/_sphinx_html/_static/gallery/special/pie.json new file mode 100644 index 000000000..b52b401ee --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/special/pie.json @@ -0,0 +1,117 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:44.304412", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "pie", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 28.000000000000004, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.1023622047244097, + 1.1023622047244093 + ], + "axes_size_px": [ + 330, + 330 + ], + "axes_position": [ + 0.325, + 0.29411764705882354, + 0.35000000000000003, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 26.0, + "bottom": 20.0, + "right": 26.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 306, + "y0": 236, + "x1": 636, + "y1": 566, + "width": 330, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 26.0, + "y0": 20.0, + "x1": 54.0, + "y1": 47.99999999999999, + "width": 28.000000000000004, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "", + "unit": "", + "scale": "linear", + "lim": [ + -1.25, + 1.25 + ], + "n_ticks": 4 + }, + "y": { + "label": "", + "unit": "", + "scale": "linear", + "lim": [ + -1.25, + 1.25 + ], + "n_ticks": 4 + } + }, + "title": "ax.pie()" +} diff --git a/src/scitex/_sphinx_html/_static/gallery/special/stx_raster.json b/src/scitex/_sphinx_html/_static/gallery/special/stx_raster.json new file mode 100644 index 000000000..41a0ad14e --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/special/stx_raster.json @@ -0,0 +1,117 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:45.106725", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_raster", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Time", + "unit": "ms", + "scale": "linear", + "lim": [ + -47.800000000000004, + 1047.8 + ], + "n_ticks": 4 + }, + "y": { + "label": "Neuron", + "unit": "", + "scale": "linear", + "lim": [ + -1.83, + 20.830000000000002 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_raster()" +} diff --git a/src/scitex/_sphinx_html/_static/gallery/special/stx_rectangle.json b/src/scitex/_sphinx_html/_static/gallery/special/stx_rectangle.json new file mode 100644 index 000000000..c5df60e0d --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/special/stx_rectangle.json @@ -0,0 +1,131 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:46.187452", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_rectangle", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Time", + "unit": "s", + "scale": "linear", + "lim": [ + -0.5, + 10.5 + ], + "n_ticks": 4 + }, + "y": { + "label": "Amplitude", + "unit": "", + "scale": "linear", + "lim": [ + -1.3061076003757186, + 1.2461590305669756 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_rectangle()", + "plot_type": "line", + "method": "plot", + "traces": [ + { + "id": "signal", + "color": "#0000ff", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "csv_columns": { + "x": "ax_00_plot_0_plot_x", + "y": "ax_00_plot_0_plot_y" + } + } + ] +} diff --git a/src/scitex/_sphinx_html/_static/gallery/statistical/errorbar.json b/src/scitex/_sphinx_html/_static/gallery/statistical/errorbar.json new file mode 100644 index 000000000..88fe48886 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/statistical/errorbar.json @@ -0,0 +1,157 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:20.116627", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "errorbar", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Condition", + "unit": "", + "scale": "linear", + "lim": [ + -0.2, + 4.2 + ], + "n_ticks": 4 + }, + "y": { + "label": "Accuracy", + "unit": "", + "scale": "linear", + "lim": [ + 0.656, + 0.9640000000000001 + ], + "n_ticks": 4 + } + }, + "title": "ax.errorbar()", + "plot_type": "bar", + "method": "bar", + "traces": [ + { + "id": "accuracy", + "color": "#0080bf", + "linestyle": "-", + "linewidth": 0.56693, + "marker": "o", + "markersize": 2.267716535433071, + "csv_columns": { + "x": "ax_00_plot_0_plot_x", + "y": "ax_00_plot_0_plot_y" + } + }, + { + "id": "line_1", + "color": "#0080bf", + "linestyle": "None", + "linewidth": 0.56693, + "marker": "_", + "markersize": 6.0, + "csv_columns": { + "x": "ax_00_plot_1_plot_x", + "y": "ax_00_plot_1_plot_y" + } + }, + { + "id": "line_2", + "color": "#0080bf", + "linestyle": "None", + "linewidth": 0.56693, + "marker": "_", + "markersize": 6.0, + "csv_columns": { + "x": "ax_00_plot_2_plot_x", + "y": "ax_00_plot_2_plot_y" + } + } + ] +} diff --git a/src/scitex/_sphinx_html/_static/gallery/statistical/stx_errorbar.json b/src/scitex/_sphinx_html/_static/gallery/statistical/stx_errorbar.json new file mode 100644 index 000000000..8a9887b7f --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/statistical/stx_errorbar.json @@ -0,0 +1,157 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:20.702495", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_errorbar", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Condition", + "unit": "", + "scale": "linear", + "lim": [ + -0.2, + 4.2 + ], + "n_ticks": 4 + }, + "y": { + "label": "Accuracy", + "unit": "", + "scale": "linear", + "lim": [ + 0.6355, + 0.9545 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_errorbar()", + "plot_type": "bar", + "method": "bar", + "traces": [ + { + "id": "line_0", + "color": "#0080bf", + "linestyle": "-", + "linewidth": 0.5669291338582677, + "marker": "o", + "markersize": 2.267716535433071, + "csv_columns": { + "x": "ax_00_plot_0_plot_x", + "y": "ax_00_plot_0_plot_y" + } + }, + { + "id": "line_1", + "color": "#0080bf", + "linestyle": "None", + "linewidth": 0.5669291338582677, + "marker": "_", + "markersize": 2.267716535433071, + "csv_columns": { + "x": "ax_00_plot_1_plot_x", + "y": "ax_00_plot_1_plot_y" + } + }, + { + "id": "line_2", + "color": "#0080bf", + "linestyle": "None", + "linewidth": 0.5669291338582677, + "marker": "_", + "markersize": 2.267716535433071, + "csv_columns": { + "x": "ax_00_plot_2_plot_x", + "y": "ax_00_plot_2_plot_y" + } + } + ] +} diff --git a/src/scitex/_sphinx_html/_static/gallery/statistical/stx_mean_ci.json b/src/scitex/_sphinx_html/_static/gallery/statistical/stx_mean_ci.json new file mode 100644 index 000000000..3881b81a3 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/statistical/stx_mean_ci.json @@ -0,0 +1,131 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:19.080539", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_mean_ci", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Time", + "unit": "ms", + "scale": "linear", + "lim": [ + -4.95, + 103.95 + ], + "n_ticks": 4 + }, + "y": { + "label": "BOLD", + "unit": "%", + "scale": "linear", + "lim": [ + -2.1628694965990682, + 2.084608084954711 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_mean_ci()", + "plot_type": "line", + "method": "plot", + "traces": [ + { + "id": "line_0", + "color": "#0080bf", + "linestyle": "-", + "linewidth": 0.56693, + "csv_columns": { + "x": "ax_00_plot_0_plot_x", + "y": "ax_00_plot_0_plot_y" + } + } + ] +} diff --git a/src/scitex/_sphinx_html/_static/gallery/statistical/stx_mean_std.json b/src/scitex/_sphinx_html/_static/gallery/statistical/stx_mean_std.json new file mode 100644 index 000000000..c964f9e3d --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/statistical/stx_mean_std.json @@ -0,0 +1,131 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:18.448799", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_mean_std", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Time", + "unit": "ms", + "scale": "linear", + "lim": [ + -4.95, + 103.95 + ], + "n_ticks": 4 + }, + "y": { + "label": "Amplitude", + "unit": "\u03bcV", + "scale": "linear", + "lim": [ + -1.5334672751360483, + 1.5107163575169889 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_mean_std()", + "plot_type": "line", + "method": "plot", + "traces": [ + { + "id": "line_0", + "color": "#0080bf", + "linestyle": "-", + "linewidth": 0.56693, + "csv_columns": { + "x": "ax_00_plot_0_plot_x", + "y": "ax_00_plot_0_plot_y" + } + } + ] +} diff --git a/src/scitex/_sphinx_html/_static/gallery/statistical/stx_median_iqr.json b/src/scitex/_sphinx_html/_static/gallery/statistical/stx_median_iqr.json new file mode 100644 index 000000000..afdf1440d --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/statistical/stx_median_iqr.json @@ -0,0 +1,131 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:19.637165", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "stx_median_iqr", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "Trial", + "unit": "", + "scale": "linear", + "lim": [ + -4.95, + 103.95 + ], + "n_ticks": 4 + }, + "y": { + "label": "RT", + "unit": "ms", + "scale": "linear", + "lim": [ + 0.7417961649939653, + 8.45154397980385 + ], + "n_ticks": 4 + } + }, + "title": "ax.stx_median_iqr()", + "plot_type": "line", + "method": "plot", + "traces": [ + { + "id": "line_0", + "color": "#0080bf", + "linestyle": "-", + "linewidth": 0.56693, + "csv_columns": { + "x": "ax_00_plot_0_plot_x", + "y": "ax_00_plot_0_plot_y" + } + } + ] +} diff --git a/src/scitex/_sphinx_html/_static/gallery/vector/quiver.json b/src/scitex/_sphinx_html/_static/gallery/vector/quiver.json new file mode 100644 index 000000000..1a68e9d5b --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/vector/quiver.json @@ -0,0 +1,117 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:41.578381", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "quiver", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "X", + "unit": "", + "scale": "linear", + "lim": [ + -3.1428571428571432, + 3.1428571428571432 + ], + "n_ticks": 4 + }, + "y": { + "label": "Y", + "unit": "", + "scale": "linear", + "lim": [ + -2.2, + 2.2 + ], + "n_ticks": 4 + } + }, + "title": "ax.quiver()" +} diff --git a/src/scitex/_sphinx_html/_static/gallery/vector/streamplot.json b/src/scitex/_sphinx_html/_static/gallery/vector/streamplot.json new file mode 100644 index 000000000..9a2083d58 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/gallery/vector/streamplot.json @@ -0,0 +1,119 @@ +{ + "metadata_version": "1.1.0", + "scitex": { + "version": "2.5.0", + "created_at": "2025-12-08T23:40:43.490077", + "created_with": "scitex.plt.subplots (mm-control)", + "mode": "publication", + "axes_size_mm": [ + 40, + 28 + ], + "position_in_grid": [ + 0, + 0 + ], + "style_mm": { + "axis_thickness_mm": 0.2, + "tick_length_mm": 0.8, + "tick_thickness_mm": 0.2, + "trace_thickness_mm": 0.2, + "marker_size_mm": 0.8, + "axis_font_size_pt": 7, + "tick_font_size_pt": 7, + "title_font_size_pt": 8, + "legend_font_size_pt": 6, + "label_pad_pt": 0.5, + "tick_pad_pt": 2.0, + "title_pad_pt": 1.0, + "font_family": "Arial", + "n_ticks": 4, + "font_family_requested": "Arial", + "font_family_actual": "Arial" + } + }, + "matplotlib": { + "version": "3.10.3" + }, + "id": "streamplot", + "dimensions": { + "figure_size_mm": [ + 80.0, + 68.0 + ], + "figure_size_inch": [ + 3.1496062992125986, + 2.677165354330709 + ], + "figure_size_px": [ + 944, + 803 + ], + "axes_size_mm": [ + 40.0, + 27.999999999999993 + ], + "axes_size_inch": [ + 1.5748031496062993, + 1.1023622047244093 + ], + "axes_size_px": [ + 472, + 330 + ], + "axes_position": [ + 0.25, + 0.29411764705882354, + 0.5, + 0.41176470588235287 + ], + "dpi": 300 + }, + "margins_mm": { + "left": 20.0, + "bottom": 20.0, + "right": 20.0, + "top": 20.000000000000007 + }, + "axes_bbox_px": { + "x0": 236, + "y0": 236, + "x1": 708, + "y1": 566, + "width": 472, + "height": 330 + }, + "axes_bbox_mm": { + "x0": 20.0, + "y0": 20.0, + "x1": 60.0, + "y1": 47.99999999999999, + "width": 40.0, + "height": 27.999999999999993 + }, + "axes": { + "x": { + "label": "X", + "unit": "", + "scale": "linear", + "lim": [ + -2.857142857142857, + 2.857142857142857 + ], + "n_ticks": 4 + }, + "y": { + "label": "Y", + "unit": "", + "scale": "linear", + "lim": [ + -2.0, + 2.0 + ], + "n_ticks": 4 + } + }, + "title": "ax.streamplot()", + "plot_type": "hist", + "method": "hist" +} diff --git a/src/scitex/_sphinx_html/_static/jquery.js b/src/scitex/_sphinx_html/_static/jquery.js new file mode 100644 index 000000000..c4c6022f2 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/jquery.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t a.language.name.localeCompare(b.language.name)); + + const languagesHTML = ` +
+
Languages
+ ${languages + .map( + (translation) => ` +
+ ${translation.language.code} +
+ `, + ) + .join("\n")} +
+ `; + return languagesHTML; + } + + function renderVersions(config) { + if (!config.versions.active.length) { + return ""; + } + const versionsHTML = ` +
+
Versions
+ ${config.versions.active + .map( + (version) => ` +
+ ${version.slug} +
+ `, + ) + .join("\n")} +
+ `; + return versionsHTML; + } + + function renderDownloads(config) { + if (!Object.keys(config.versions.current.downloads).length) { + return ""; + } + const downloadsNameDisplay = { + pdf: "PDF", + epub: "Epub", + htmlzip: "HTML", + }; + + const downloadsHTML = ` +
+
Downloads
+ ${Object.entries(config.versions.current.downloads) + .map( + ([name, url]) => ` +
+ ${downloadsNameDisplay[name]} +
+ `, + ) + .join("\n")} +
+ `; + return downloadsHTML; + } + + document.addEventListener("readthedocs-addons-data-ready", function (event) { + const config = event.detail.data(); + + const flyout = ` +
+ + Read the Docs + v: ${config.versions.current.slug} + + +
+
+ ${renderLanguages(config)} + ${renderVersions(config)} + ${renderDownloads(config)} +
+
On Read the Docs
+
+ Project Home +
+
+ Builds +
+
+ Downloads +
+
+
+
Search
+
+
+ +
+
+
+
+ + Hosted by Read the Docs + +
+
+ `; + + // Inject the generated flyout into the body HTML element. + document.body.insertAdjacentHTML("beforeend", flyout); + + // Trigger the Read the Docs Addons Search modal when clicking on the "Search docs" input from inside the flyout. + document + .querySelector("#flyout-search-form") + .addEventListener("focusin", () => { + const event = new CustomEvent("readthedocs-search-show"); + document.dispatchEvent(event); + }); + }) +} + +if (themeLanguageSelector || themeVersionSelector) { + function onSelectorSwitch(event) { + const option = event.target.selectedIndex; + const item = event.target.options[option]; + window.location.href = item.dataset.url; + } + + document.addEventListener("readthedocs-addons-data-ready", function (event) { + const config = event.detail.data(); + + const versionSwitch = document.querySelector( + "div.switch-menus > div.version-switch", + ); + if (themeVersionSelector) { + let versions = config.versions.active; + if (config.versions.current.hidden || config.versions.current.type === "external") { + versions.unshift(config.versions.current); + } + const versionSelect = ` + + `; + + versionSwitch.innerHTML = versionSelect; + versionSwitch.firstElementChild.addEventListener("change", onSelectorSwitch); + } + + const languageSwitch = document.querySelector( + "div.switch-menus > div.language-switch", + ); + + if (themeLanguageSelector) { + if (config.projects.translations.length) { + // Add the current language to the options on the selector + let languages = config.projects.translations.concat( + config.projects.current, + ); + languages = languages.sort((a, b) => + a.language.name.localeCompare(b.language.name), + ); + + const languageSelect = ` + + `; + + languageSwitch.innerHTML = languageSelect; + languageSwitch.firstElementChild.addEventListener("change", onSelectorSwitch); + } + else { + languageSwitch.remove(); + } + } + }); +} + +document.addEventListener("readthedocs-addons-data-ready", function (event) { + // Trigger the Read the Docs Addons Search modal when clicking on "Search docs" input from the topnav. + document + .querySelector("[role='search'] input") + .addEventListener("focusin", () => { + const event = new CustomEvent("readthedocs-search-show"); + document.dispatchEvent(event); + }); +}); \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_static/language_data.js b/src/scitex/_sphinx_html/_static/language_data.js new file mode 100644 index 000000000..577678642 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/language_data.js @@ -0,0 +1,13 @@ +/* + * This script contains the language-specific data used by searchtools.js, + * namely the set of stopwords, stemmer, scorer and splitter. + */ + +const stopwords = new Set(["a", "about", "above", "after", "again", "against", "all", "am", "an", "and", "any", "are", "aren't", "as", "at", "be", "because", "been", "before", "being", "below", "between", "both", "but", "by", "can't", "cannot", "could", "couldn't", "did", "didn't", "do", "does", "doesn't", "doing", "don't", "down", "during", "each", "few", "for", "from", "further", "had", "hadn't", "has", "hasn't", "have", "haven't", "having", "he", "he'd", "he'll", "he's", "her", "here", "here's", "hers", "herself", "him", "himself", "his", "how", "how's", "i", "i'd", "i'll", "i'm", "i've", "if", "in", "into", "is", "isn't", "it", "it's", "its", "itself", "let's", "me", "more", "most", "mustn't", "my", "myself", "no", "nor", "not", "of", "off", "on", "once", "only", "or", "other", "ought", "our", "ours", "ourselves", "out", "over", "own", "same", "shan't", "she", "she'd", "she'll", "she's", "should", "shouldn't", "so", "some", "such", "than", "that", "that's", "the", "their", "theirs", "them", "themselves", "then", "there", "there's", "these", "they", "they'd", "they'll", "they're", "they've", "this", "those", "through", "to", "too", "under", "until", "up", "very", "was", "wasn't", "we", "we'd", "we'll", "we're", "we've", "were", "weren't", "what", "what's", "when", "when's", "where", "where's", "which", "while", "who", "who's", "whom", "why", "why's", "with", "won't", "would", "wouldn't", "you", "you'd", "you'll", "you're", "you've", "your", "yours", "yourself", "yourselves"]); +window.stopwords = stopwords; // Export to global scope + + +/* Non-minified versions are copied as separate JavaScript files, if available */ +BaseStemmer=function(){this.current="",this.cursor=0,this.limit=0,this.limit_backward=0,this.bra=0,this.ket=0,this.setCurrent=function(t){this.current=t,this.cursor=0,this.limit=this.current.length,this.limit_backward=0,this.bra=this.cursor,this.ket=this.limit},this.getCurrent=function(){return this.current},this.copy_from=function(t){this.current=t.current,this.cursor=t.cursor,this.limit=t.limit,this.limit_backward=t.limit_backward,this.bra=t.bra,this.ket=t.ket},this.in_grouping=function(t,r,i){return!(this.cursor>=this.limit||i<(i=this.current.charCodeAt(this.cursor))||i>>3]&1<<(7&i))||(this.cursor++,0))},this.go_in_grouping=function(t,r,i){for(;this.cursor>>3]&1<<(7&s)))return!0;this.cursor++}return!1},this.in_grouping_b=function(t,r,i){return!(this.cursor<=this.limit_backward||i<(i=this.current.charCodeAt(this.cursor-1))||i>>3]&1<<(7&i))||(this.cursor--,0))},this.go_in_grouping_b=function(t,r,i){for(;this.cursor>this.limit_backward;){var s=this.current.charCodeAt(this.cursor-1);if(i>>3]&1<<(7&s)))return!0;this.cursor--}return!1},this.out_grouping=function(t,r,i){return!(this.cursor>=this.limit)&&(i<(i=this.current.charCodeAt(this.cursor))||i>>3]&1<<(7&i)))&&(this.cursor++,!0)},this.go_out_grouping=function(t,r,i){for(;this.cursor>>3]&1<<(7&s)))return!0;this.cursor++}return!1},this.out_grouping_b=function(t,r,i){return!(this.cursor<=this.limit_backward)&&(i<(i=this.current.charCodeAt(this.cursor-1))||i>>3]&1<<(7&i)))&&(this.cursor--,!0)},this.go_out_grouping_b=function(t,r,i){for(;this.cursor>this.limit_backward;){var s=this.current.charCodeAt(this.cursor-1);if(s<=i&&r<=s&&0!=(t[(s-=r)>>>3]&1<<(7&s)))return!0;this.cursor--}return!1},this.eq_s=function(t){return!(this.limit-this.cursor>>1),o=0,a=e=(l=t[r])[0].length){if(this.cursor=s+l[0].length,l.length<4)return l[2];var g=l[3](this);if(this.cursor=s+l[0].length,g)return l[2]}}while(0<=(r=l[1]));return 0},this.find_among_b=function(t){for(var r=0,i=t.length,s=this.cursor,h=this.limit_backward,e=0,n=0,c=!1;;){for(var u,o=r+(i-r>>1),a=0,l=e=(u=t[r])[0].length){if(this.cursor=s-u[0].length,u.length<4)return u[2];var g=u[3](this);if(this.cursor=s-u[0].length,g)return u[2]}}while(0<=(r=u[1]));return 0},this.replace_s=function(t,r,i){var s=i.length-(r-t);return this.current=this.current.slice(0,t)+i+this.current.slice(r),this.limit+=s,this.cursor>=r?this.cursor+=s:this.cursor>t&&(this.cursor=t),s},this.slice_check=function(){return!(this.bra<0||this.bra>this.ket||this.ket>this.limit||this.limit>this.current.length)},this.slice_from=function(t){var r=!1;return this.slice_check()&&(this.replace_s(this.bra,this.ket,t),r=!0),r},this.slice_del=function(){return this.slice_from("")},this.insert=function(t,r,i){r=this.replace_s(t,r,i);t<=this.bra&&(this.bra+=r),t<=this.ket&&(this.ket+=r)},this.slice_to=function(){var t="";return t=this.slice_check()?this.current.slice(this.bra,this.ket):t},this.assign_to=function(){return this.current.slice(0,this.limit)}}; +var EnglishStemmer=function(){var a=new BaseStemmer,c=[["arsen",-1,-1],["commun",-1,-1],["emerg",-1,-1],["gener",-1,-1],["later",-1,-1],["organ",-1,-1],["past",-1,-1],["univers",-1,-1]],o=[["'",-1,1],["'s'",0,1],["'s",-1,1]],u=[["ied",-1,2],["s",-1,3],["ies",1,2],["sses",1,1],["ss",1,-1],["us",1,-1]],t=[["succ",-1,1],["proc",-1,1],["exc",-1,1]],l=[["even",-1,2],["cann",-1,2],["inn",-1,2],["earr",-1,2],["herr",-1,2],["out",-1,2],["y",-1,1]],n=[["",-1,-1],["ed",0,2],["eed",1,1],["ing",0,3],["edly",0,2],["eedly",4,1],["ingly",0,2]],f=[["",-1,3],["bb",0,2],["dd",0,2],["ff",0,2],["gg",0,2],["bl",0,1],["mm",0,2],["nn",0,2],["pp",0,2],["rr",0,2],["at",0,1],["tt",0,2],["iz",0,1]],_=[["anci",-1,3],["enci",-1,2],["ogi",-1,14],["li",-1,16],["bli",3,12],["abli",4,4],["alli",3,8],["fulli",3,9],["lessli",3,15],["ousli",3,10],["entli",3,5],["aliti",-1,8],["biliti",-1,12],["iviti",-1,11],["tional",-1,1],["ational",14,7],["alism",-1,8],["ation",-1,7],["ization",17,6],["izer",-1,6],["ator",-1,7],["iveness",-1,11],["fulness",-1,9],["ousness",-1,10],["ogist",-1,13]],m=[["icate",-1,4],["ative",-1,6],["alize",-1,3],["iciti",-1,4],["ical",-1,4],["tional",-1,1],["ational",5,2],["ful",-1,5],["ness",-1,5]],b=[["ic",-1,1],["ance",-1,1],["ence",-1,1],["able",-1,1],["ible",-1,1],["ate",-1,1],["ive",-1,1],["ize",-1,1],["iti",-1,1],["al",-1,1],["ism",-1,1],["ion",-1,2],["er",-1,1],["ous",-1,1],["ant",-1,1],["ent",-1,1],["ment",15,1],["ement",16,1]],k=[["e",-1,1],["l",-1,2]],g=[["andes",-1,-1],["atlas",-1,-1],["bias",-1,-1],["cosmos",-1,-1],["early",-1,5],["gently",-1,3],["howe",-1,-1],["idly",-1,2],["news",-1,-1],["only",-1,6],["singly",-1,7],["skies",-1,1],["sky",-1,-1],["ugly",-1,4]],d=[17,64],v=[17,65,16,1],i=[1,17,65,208,1],w=[55,141,2],p=!1,y=0,h=0;function q(){var r=a.limit-a.cursor;return!!(a.out_grouping_b(i,89,121)&&a.in_grouping_b(v,97,121)&&a.out_grouping_b(v,97,121)||(a.cursor=a.limit-r,a.out_grouping_b(v,97,121)&&a.in_grouping_b(v,97,121)&&!(a.cursor>a.limit_backward))||(a.cursor=a.limit-r,a.eq_s_b("past")))}function z(){return h<=a.cursor}function Y(){return y<=a.cursor}this.stem=function(){var r=a.cursor;if(!(()=>{var r;if(a.bra=a.cursor,0!=(r=a.find_among(g))&&(a.ket=a.cursor,!(a.cursora.limit)a.cursor=i;else{a.cursor=e,a.cursor=r,(()=>{p=!1;var r=a.cursor;if(a.bra=a.cursor,!a.eq_s("'")||(a.ket=a.cursor,a.slice_del())){a.cursor=r;r=a.cursor;if(a.bra=a.cursor,a.eq_s("y")){if(a.ket=a.cursor,!a.slice_from("Y"))return;p=!0}a.cursor=r;for(r=a.cursor;;){var i=a.cursor;r:{for(;;){var e=a.cursor;if(a.in_grouping(v,97,121)&&(a.bra=a.cursor,a.eq_s("y"))){a.ket=a.cursor,a.cursor=e;break}if(a.cursor=e,a.cursor>=a.limit)break r;a.cursor++}if(!a.slice_from("Y"))return;p=!0;continue}a.cursor=i;break}a.cursor=r}})(),h=a.limit,y=a.limit;i=a.cursor;r:{var s=a.cursor;if(0==a.find_among(c)){if(a.cursor=s,!a.go_out_grouping(v,97,121))break r;if(a.cursor++,!a.go_in_grouping(v,97,121))break r;a.cursor++}h=a.cursor,a.go_out_grouping(v,97,121)&&(a.cursor++,a.go_in_grouping(v,97,121))&&(a.cursor++,y=a.cursor)}a.cursor=i,a.limit_backward=a.cursor,a.cursor=a.limit;var e=a.limit-a.cursor,r=((()=>{var r=a.limit-a.cursor;if(a.ket=a.cursor,0==a.find_among_b(o))a.cursor=a.limit-r;else if(a.bra=a.cursor,!a.slice_del())return;if(a.ket=a.cursor,0!=(r=a.find_among_b(u)))switch(a.bra=a.cursor,r){case 1:if(a.slice_from("ss"))break;return;case 2:r:{var i=a.limit-a.cursor,e=a.cursor-2;if(!(e{a.ket=a.cursor,o=a.find_among_b(n),a.bra=a.cursor;r:{var r=a.limit-a.cursor;i:{switch(o){case 1:var i=a.limit-a.cursor;e:{var e=a.limit-a.cursor;if(0==a.find_among_b(t)||a.cursor>a.limit_backward){if(a.cursor=a.limit-e,!z())break e;if(!a.slice_from("ee"))return}}a.cursor=a.limit-i;break;case 2:break i;case 3:if(0==(o=a.find_among_b(l)))break i;switch(o){case 1:var s=a.limit-a.cursor;if(!a.out_grouping_b(v,97,121))break i;if(a.cursor>a.limit_backward)break i;if(a.cursor=a.limit-s,a.bra=a.cursor,a.slice_from("ie"))break;return;case 2:if(a.cursor>a.limit_backward)break i}}break r}a.cursor=a.limit-r;var c=a.limit-a.cursor;if(!a.go_out_grouping_b(v,97,121))return;if(a.cursor--,a.cursor=a.limit-c,!a.slice_del())return;a.ket=a.cursor,a.bra=a.cursor;var o,c=a.limit-a.cursor;switch(o=a.find_among_b(f)){case 1:return a.slice_from("e");case 2:var u=a.limit-a.cursor;if(a.in_grouping_b(d,97,111)&&!(a.cursor>a.limit_backward))return;a.cursor=a.limit-u;break;case 3:return a.cursor!=h||(u=a.limit-a.cursor,q()&&(a.cursor=a.limit-u,a.slice_from("e")))}if(a.cursor=a.limit-c,a.ket=a.cursor,a.cursor<=a.limit_backward)return;if(a.cursor--,a.bra=a.cursor,!a.slice_del())return}})(),a.cursor=a.limit-r,a.limit-a.cursor),r=(a.ket=a.cursor,e=a.limit-a.cursor,(a.eq_s_b("y")||(a.cursor=a.limit-e,a.eq_s_b("Y")))&&(a.bra=a.cursor,a.out_grouping_b(v,97,121))&&a.cursor>a.limit_backward&&a.slice_from("i"),a.cursor=a.limit-i,a.limit-a.cursor),e=((()=>{var r;if(a.ket=a.cursor,0!=(r=a.find_among_b(_))&&(a.bra=a.cursor,z()))switch(r){case 1:if(a.slice_from("tion"))break;return;case 2:if(a.slice_from("ence"))break;return;case 3:if(a.slice_from("ance"))break;return;case 4:if(a.slice_from("able"))break;return;case 5:if(a.slice_from("ent"))break;return;case 6:if(a.slice_from("ize"))break;return;case 7:if(a.slice_from("ate"))break;return;case 8:if(a.slice_from("al"))break;return;case 9:if(a.slice_from("ful"))break;return;case 10:if(a.slice_from("ous"))break;return;case 11:if(a.slice_from("ive"))break;return;case 12:if(a.slice_from("ble"))break;return;case 13:if(a.slice_from("og"))break;return;case 14:if(!a.eq_s_b("l"))return;if(a.slice_from("og"))break;return;case 15:if(a.slice_from("less"))break;return;case 16:if(!a.in_grouping_b(w,99,116))return;if(a.slice_del())break}})(),a.cursor=a.limit-r,a.limit-a.cursor),i=((()=>{var r;if(a.ket=a.cursor,0!=(r=a.find_among_b(m))&&(a.bra=a.cursor,z()))switch(r){case 1:if(a.slice_from("tion"))break;return;case 2:if(a.slice_from("ate"))break;return;case 3:if(a.slice_from("al"))break;return;case 4:if(a.slice_from("ic"))break;return;case 5:if(a.slice_del())break;return;case 6:if(!Y())return;if(a.slice_del())break}})(),a.cursor=a.limit-e,a.limit-a.cursor),r=((()=>{var r;if(a.ket=a.cursor,0!=(r=a.find_among_b(b))&&(a.bra=a.cursor,Y()))switch(r){case 1:if(a.slice_del())break;return;case 2:var i=a.limit-a.cursor;if(!a.eq_s_b("s")&&(a.cursor=a.limit-i,!a.eq_s_b("t")))return;if(a.slice_del())break}})(),a.cursor=a.limit-i,a.limit-a.cursor),e=((()=>{var r;if(a.ket=a.cursor,0!=(r=a.find_among_b(k)))switch(a.bra=a.cursor,r){case 1:if(!Y()){if(!z())return;var i=a.limit-a.cursor;if(q())return;a.cursor=a.limit-i}if(a.slice_del())break;return;case 2:if(!Y())return;if(!a.eq_s_b("l"))return;if(a.slice_del())break}})(),a.cursor=a.limit-r,a.cursor=a.limit_backward,a.cursor);(()=>{if(p)for(;;){var r=a.cursor;r:{for(;;){var i=a.cursor;if(a.bra=a.cursor,a.eq_s("Y")){a.ket=a.cursor,a.cursor=i;break}if(a.cursor=i,a.cursor>=a.limit)break r;a.cursor++}if(a.slice_from("y"))continue;return}a.cursor=r;break}})(),a.cursor=e}}return!0},this.stemWord=function(r){return a.setCurrent(r),this.stem(),a.getCurrent()}}; +window.Stemmer = EnglishStemmer; diff --git a/src/scitex/_sphinx_html/_static/nbsphinx-broken-thumbnail.svg b/src/scitex/_sphinx_html/_static/nbsphinx-broken-thumbnail.svg new file mode 100644 index 000000000..4919ca882 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/nbsphinx-broken-thumbnail.svg @@ -0,0 +1,9 @@ + + + + diff --git a/src/scitex/_sphinx_html/_static/nbsphinx-code-cells.css b/src/scitex/_sphinx_html/_static/nbsphinx-code-cells.css new file mode 100644 index 000000000..a3fb27c30 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/nbsphinx-code-cells.css @@ -0,0 +1,259 @@ +/* remove conflicting styling from Sphinx themes */ +div.nbinput.container div.prompt *, +div.nboutput.container div.prompt *, +div.nbinput.container div.input_area pre, +div.nboutput.container div.output_area pre, +div.nbinput.container div.input_area .highlight, +div.nboutput.container div.output_area .highlight { + border: none; + padding: 0; + margin: 0; + box-shadow: none; +} + +div.nbinput.container > div[class*=highlight], +div.nboutput.container > div[class*=highlight] { + margin: 0; +} + +div.nbinput.container div.prompt *, +div.nboutput.container div.prompt * { + background: none; +} + +div.nboutput.container div.output_area .highlight, +div.nboutput.container div.output_area pre { + background: unset; +} + +div.nboutput.container div.output_area div.highlight { + color: unset; /* override Pygments text color */ +} + +/* avoid gaps between output lines */ +div.nboutput.container div[class*=highlight] pre { + line-height: normal; +} + +/* input/output containers */ +div.nbinput.container, +div.nboutput.container { + display: -webkit-flex; + display: flex; + align-items: flex-start; + margin: 0; + width: 100%; +} +@media (max-width: 540px) { + div.nbinput.container, + div.nboutput.container { + flex-direction: column; + } +} + +/* input container */ +div.nbinput.container { + padding-top: 5px; +} + +/* last container */ +div.nblast.container { + padding-bottom: 5px; +} + +/* input prompt */ +div.nbinput.container div.prompt pre, +/* for sphinx_immaterial theme: */ +div.nbinput.container div.prompt pre > code { + color: #307FC1; +} + +/* output prompt */ +div.nboutput.container div.prompt pre, +/* for sphinx_immaterial theme: */ +div.nboutput.container div.prompt pre > code { + color: #BF5B3D; +} + +/* all prompts */ +div.nbinput.container div.prompt, +div.nboutput.container div.prompt { + width: 4.5ex; + padding-top: 5px; + position: relative; + user-select: none; +} + +div.nbinput.container div.prompt > div, +div.nboutput.container div.prompt > div { + position: absolute; + right: 0; + margin-right: 0.3ex; +} + +@media (max-width: 540px) { + div.nbinput.container div.prompt, + div.nboutput.container div.prompt { + width: unset; + text-align: left; + padding: 0.4em; + } + div.nboutput.container div.prompt.empty { + padding: 0; + } + + div.nbinput.container div.prompt > div, + div.nboutput.container div.prompt > div { + position: unset; + } +} + +/* disable scrollbars and line breaks on prompts */ +div.nbinput.container div.prompt pre, +div.nboutput.container div.prompt pre { + overflow: hidden; + white-space: pre; +} + +/* input/output area */ +div.nbinput.container div.input_area, +div.nboutput.container div.output_area { + -webkit-flex: 1; + flex: 1; + overflow: auto; +} +@media (max-width: 540px) { + div.nbinput.container div.input_area, + div.nboutput.container div.output_area { + width: 100%; + } +} + +/* input area */ +div.nbinput.container div.input_area { + border: 1px solid #e0e0e0; + border-radius: 2px; + /*background: #f5f5f5;*/ +} + +/* override MathJax center alignment in output cells */ +div.nboutput.container div[class*=MathJax] { + text-align: left !important; +} + +/* override sphinx.ext.imgmath center alignment in output cells */ +div.nboutput.container div.math p { + text-align: left; +} + +/* standard error */ +div.nboutput.container div.output_area.stderr { + background: #fdd; +} + +/* ANSI colors */ +.ansi-black-fg { color: #3E424D; } +.ansi-black-bg { background-color: #3E424D; } +.ansi-black-intense-fg { color: #282C36; } +.ansi-black-intense-bg { background-color: #282C36; } +.ansi-red-fg { color: #E75C58; } +.ansi-red-bg { background-color: #E75C58; } +.ansi-red-intense-fg { color: #B22B31; } +.ansi-red-intense-bg { background-color: #B22B31; } +.ansi-green-fg { color: #00A250; } +.ansi-green-bg { background-color: #00A250; } +.ansi-green-intense-fg { color: #007427; } +.ansi-green-intense-bg { background-color: #007427; } +.ansi-yellow-fg { color: #DDB62B; } +.ansi-yellow-bg { background-color: #DDB62B; } +.ansi-yellow-intense-fg { color: #B27D12; } +.ansi-yellow-intense-bg { background-color: #B27D12; } +.ansi-blue-fg { color: #208FFB; } +.ansi-blue-bg { background-color: #208FFB; } +.ansi-blue-intense-fg { color: #0065CA; } +.ansi-blue-intense-bg { background-color: #0065CA; } +.ansi-magenta-fg { color: #D160C4; } +.ansi-magenta-bg { background-color: #D160C4; } +.ansi-magenta-intense-fg { color: #A03196; } +.ansi-magenta-intense-bg { background-color: #A03196; } +.ansi-cyan-fg { color: #60C6C8; } +.ansi-cyan-bg { background-color: #60C6C8; } +.ansi-cyan-intense-fg { color: #258F8F; } +.ansi-cyan-intense-bg { background-color: #258F8F; } +.ansi-white-fg { color: #C5C1B4; } +.ansi-white-bg { background-color: #C5C1B4; } +.ansi-white-intense-fg { color: #A1A6B2; } +.ansi-white-intense-bg { background-color: #A1A6B2; } + +.ansi-default-inverse-fg { color: #FFFFFF; } +.ansi-default-inverse-bg { background-color: #000000; } + +.ansi-bold { font-weight: bold; } +.ansi-underline { text-decoration: underline; } + + +div.nbinput.container div.input_area div[class*=highlight] > pre, +div.nboutput.container div.output_area div[class*=highlight] > pre, +div.nboutput.container div.output_area div[class*=highlight].math, +div.nboutput.container div.output_area.rendered_html, +div.nboutput.container div.output_area > div.output_javascript, +div.nboutput.container div.output_area:not(.rendered_html) > img{ + padding: 5px; + margin: 0; +} + +/* fix copybtn overflow problem in chromium (needed for 'sphinx_copybutton') */ +div.nbinput.container div.input_area > div[class^='highlight'], +div.nboutput.container div.output_area > div[class^='highlight']{ + overflow-y: hidden; +} + +/* hide copy button on prompts for 'sphinx_copybutton' extension ... */ +.prompt .copybtn, +/* ... and 'sphinx_immaterial' theme */ +.prompt .md-clipboard.md-icon { + display: none; +} + +/* Some additional styling taken form the Jupyter notebook CSS */ +.jp-RenderedHTMLCommon table, +div.rendered_html table { + border: none; + border-collapse: collapse; + border-spacing: 0; + color: black; + font-size: 12px; + table-layout: fixed; +} +.jp-RenderedHTMLCommon thead, +div.rendered_html thead { + border-bottom: 1px solid black; + vertical-align: bottom; +} +.jp-RenderedHTMLCommon tr, +.jp-RenderedHTMLCommon th, +.jp-RenderedHTMLCommon td, +div.rendered_html tr, +div.rendered_html th, +div.rendered_html td { + text-align: right; + vertical-align: middle; + padding: 0.5em 0.5em; + line-height: normal; + white-space: normal; + max-width: none; + border: none; +} +.jp-RenderedHTMLCommon th, +div.rendered_html th { + font-weight: bold; +} +.jp-RenderedHTMLCommon tbody tr:nth-child(odd), +div.rendered_html tbody tr:nth-child(odd) { + background: #f5f5f5; +} +.jp-RenderedHTMLCommon tbody tr:hover, +div.rendered_html tbody tr:hover { + background: rgba(66, 165, 245, 0.2); +} + diff --git a/src/scitex/_sphinx_html/_static/nbsphinx-gallery.css b/src/scitex/_sphinx_html/_static/nbsphinx-gallery.css new file mode 100644 index 000000000..365c27a96 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/nbsphinx-gallery.css @@ -0,0 +1,31 @@ +.nbsphinx-gallery { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); + gap: 5px; + margin-top: 1em; + margin-bottom: 1em; +} + +.nbsphinx-gallery > a { + padding: 5px; + border: 1px dotted currentColor; + border-radius: 2px; + text-align: center; +} + +.nbsphinx-gallery > a:hover { + border-style: solid; +} + +.nbsphinx-gallery img { + max-width: 100%; + max-height: 100%; +} + +.nbsphinx-gallery > a > div:first-child { + display: flex; + align-items: start; + justify-content: center; + height: 120px; + margin-bottom: 5px; +} diff --git a/src/scitex/_sphinx_html/_static/nbsphinx-no-thumbnail.svg b/src/scitex/_sphinx_html/_static/nbsphinx-no-thumbnail.svg new file mode 100644 index 000000000..9dca7588f --- /dev/null +++ b/src/scitex/_sphinx_html/_static/nbsphinx-no-thumbnail.svg @@ -0,0 +1,9 @@ + + + + diff --git a/src/scitex/_sphinx_html/_static/pygments.css b/src/scitex/_sphinx_html/_static/pygments.css new file mode 100644 index 000000000..6f8b210a1 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/pygments.css @@ -0,0 +1,75 @@ +pre { line-height: 125%; } +td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #f8f8f8; } +.highlight .c { color: #3D7B7B; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #F00 } /* Error */ +.highlight .k { color: #008000; font-weight: bold } /* Keyword */ +.highlight .o { color: #666 } /* Operator */ +.highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #9C6500 } /* Comment.Preproc */ +.highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +.highlight .gr { color: #E40000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #008400 } /* Generic.Inserted */ +.highlight .go { color: #717171 } /* Generic.Output */ +.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #04D } /* Generic.Traceback */ +.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #008000 } /* Keyword.Pseudo */ +.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #B00040 } /* Keyword.Type */ +.highlight .m { color: #666 } /* Literal.Number */ +.highlight .s { color: #BA2121 } /* Literal.String */ +.highlight .na { color: #687822 } /* Name.Attribute */ +.highlight .nb { color: #008000 } /* Name.Builtin */ +.highlight .nc { color: #00F; font-weight: bold } /* Name.Class */ +.highlight .no { color: #800 } /* Name.Constant */ +.highlight .nd { color: #A2F } /* Name.Decorator */ +.highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #00F } /* Name.Function */ +.highlight .nl { color: #767600 } /* Name.Label */ +.highlight .nn { color: #00F; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #19177C } /* Name.Variable */ +.highlight .ow { color: #A2F; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #BBB } /* Text.Whitespace */ +.highlight .mb { color: #666 } /* Literal.Number.Bin */ +.highlight .mf { color: #666 } /* Literal.Number.Float */ +.highlight .mh { color: #666 } /* Literal.Number.Hex */ +.highlight .mi { color: #666 } /* Literal.Number.Integer */ +.highlight .mo { color: #666 } /* Literal.Number.Oct */ +.highlight .sa { color: #BA2121 } /* Literal.String.Affix */ +.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ +.highlight .sc { color: #BA2121 } /* Literal.String.Char */ +.highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ +.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ +.highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ +.highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ +.highlight .sx { color: #008000 } /* Literal.String.Other */ +.highlight .sr { color: #A45A77 } /* Literal.String.Regex */ +.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ +.highlight .ss { color: #19177C } /* Literal.String.Symbol */ +.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #00F } /* Name.Function.Magic */ +.highlight .vc { color: #19177C } /* Name.Variable.Class */ +.highlight .vg { color: #19177C } /* Name.Variable.Global */ +.highlight .vi { color: #19177C } /* Name.Variable.Instance */ +.highlight .vm { color: #19177C } /* Name.Variable.Magic */ +.highlight .il { color: #666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/src/scitex/_sphinx_html/_static/searchtools.js b/src/scitex/_sphinx_html/_static/searchtools.js new file mode 100644 index 000000000..e29b1c754 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/searchtools.js @@ -0,0 +1,693 @@ +/* + * Sphinx JavaScript utilities for the full-text search. + */ +"use strict"; + +/** + * Simple result scoring code. + */ +if (typeof Scorer === "undefined") { + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [docname, title, anchor, descr, score, filename] + // and returns the new score. + /* + score: result => { + const [docname, title, anchor, descr, score, filename, kind] = result + return score + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: { + 0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5, // used to be unimportantResults + }, + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2, + }; +} + +// Global search result kind enum, used by themes to style search results. +// prettier-ignore +class SearchResultKind { + static get index() { return "index"; } + static get object() { return "object"; } + static get text() { return "text"; } + static get title() { return "title"; } +} + +const _removeChildren = (element) => { + while (element && element.lastChild) element.removeChild(element.lastChild); +}; + +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + */ +const _escapeRegExp = (string) => + string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string + +const _escapeHTML = (text) => { + return text + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll('"', """) + .replaceAll("'", "'"); +}; + +const _displayItem = (item, searchTerms, highlightTerms) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + const contentRoot = document.documentElement.dataset.content_root; + + const [docName, title, anchor, descr, score, _filename, kind] = item; + + let listItem = document.createElement("li"); + // Add a class representing the item's type: + // can be used by a theme's CSS selector for styling + // See SearchResultKind for the class names. + listItem.classList.add(`kind-${kind}`); + let requestUrl; + let linkUrl; + if (docBuilder === "dirhtml") { + // dirhtml builder + let dirname = docName + "/"; + if (dirname.match(/\/index\/$/)) + dirname = dirname.substring(0, dirname.length - 6); + else if (dirname === "index/") dirname = ""; + requestUrl = contentRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = contentRoot + docName + docFileSuffix; + linkUrl = docName + docLinkSuffix; + } + let linkEl = listItem.appendChild(document.createElement("a")); + linkEl.href = linkUrl + anchor; + linkEl.dataset.score = score; + linkEl.innerHTML = _escapeHTML(title); + if (descr) { + listItem.appendChild(document.createElement("span")).innerHTML = + ` (${_escapeHTML(descr)})`; + // highlight search terms in the description + if (SPHINX_HIGHLIGHT_ENABLED) + // SPHINX_HIGHLIGHT_ENABLED is set in sphinx_highlight.js + highlightTerms.forEach((term) => + _highlightText(listItem, term, "highlighted"), + ); + } else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms, anchor), + ); + // highlight search terms in the summary + if (SPHINX_HIGHLIGHT_ENABLED) + // SPHINX_HIGHLIGHT_ENABLED is set in sphinx_highlight.js + highlightTerms.forEach((term) => + _highlightText(listItem, term, "highlighted"), + ); + }); + Search.output.appendChild(listItem); +}; +const _finishSearch = (resultCount) => { + Search.stopPulse(); + Search.title.innerText = _("Search Results"); + if (!resultCount) + Search.status.innerText = Documentation.gettext( + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.", + ); + else + Search.status.innerText = Documentation.ngettext( + "Search finished, found one page matching the search query.", + "Search finished, found ${resultCount} pages matching the search query.", + resultCount, + ).replace("${resultCount}", resultCount); +}; +const _displayNextItem = ( + results, + resultCount, + searchTerms, + highlightTerms, +) => { + // results left, load the summary and display it + // this is intended to be dynamic (don't sub resultsCount) + if (results.length) { + _displayItem(results.pop(), searchTerms, highlightTerms); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), + 5, + ); + } + // search finished, update title and status message + else _finishSearch(resultCount); +}; +// Helper function used by query() to order search results. +// Each input is an array of [docname, title, anchor, descr, score, filename, kind]. +// Order the results by score (in opposite order of appearance, since the +// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically. +const _orderResultsByScoreThenName = (a, b) => { + const leftScore = a[4]; + const rightScore = b[4]; + if (leftScore === rightScore) { + // same score: sort alphabetically + const leftTitle = a[1].toLowerCase(); + const rightTitle = b[1].toLowerCase(); + if (leftTitle === rightTitle) return 0; + return leftTitle > rightTitle ? -1 : 1; // inverted is intentional + } + return leftScore > rightScore ? 1 : -1; +}; + +/** + * Default splitQuery function. Can be overridden in ``sphinx.search`` with a + * custom function per language. + * + * The regular expression works by splitting the string on consecutive characters + * that are not Unicode letters, numbers, underscores, or emoji characters. + * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + */ +if (typeof splitQuery === "undefined") { + var splitQuery = (query) => + query + .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) + .filter((term) => term); // remove remaining empty strings +} + +/** + * Search Module + */ +const Search = { + _index: null, + _queued_query: null, + _pulse_status: -1, + + htmlToText: (htmlString, anchor) => { + const htmlElement = new DOMParser().parseFromString( + htmlString, + "text/html", + ); + for (const removalQuery of [".headerlink", "script", "style"]) { + htmlElement.querySelectorAll(removalQuery).forEach((el) => { + el.remove(); + }); + } + if (anchor) { + const anchorContent = htmlElement.querySelector( + `[role="main"] ${anchor}`, + ); + if (anchorContent) return anchorContent.textContent; + + console.warn( + `Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.`, + ); + } + + // if anchor not specified or not found, fall back to main content + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent) return docContent.textContent; + + console.warn( + "Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template.", + ); + return ""; + }, + + init: () => { + const query = new URLSearchParams(window.location.search).get("q"); + document + .querySelectorAll('input[name="q"]') + .forEach((el) => (el.value = query)); + if (query) Search.performSearch(query); + }, + + loadIndex: (url) => + (document.body.appendChild(document.createElement("script")).src = url), + + setIndex: (index) => { + Search._index = index; + if (Search._queued_query !== null) { + const query = Search._queued_query; + Search._queued_query = null; + Search.query(query); + } + }, + + hasIndex: () => Search._index !== null, + + deferQuery: (query) => (Search._queued_query = query), + + stopPulse: () => (Search._pulse_status = -1), + + startPulse: () => { + if (Search._pulse_status >= 0) return; + + const pulse = () => { + Search._pulse_status = (Search._pulse_status + 1) % 4; + Search.dots.innerText = ".".repeat(Search._pulse_status); + if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch: (query) => { + // create the required interface elements + const searchText = document.createElement("h2"); + searchText.textContent = _("Searching"); + const searchSummary = document.createElement("p"); + searchSummary.classList.add("search-summary"); + searchSummary.innerText = ""; + const searchList = document.createElement("ul"); + searchList.setAttribute("role", "list"); + searchList.classList.add("search"); + + const out = document.getElementById("search-results"); + Search.title = out.appendChild(searchText); + Search.dots = Search.title.appendChild(document.createElement("span")); + Search.status = out.appendChild(searchSummary); + Search.output = out.appendChild(searchList); + + const searchProgress = document.getElementById("search-progress"); + // Some themes don't use the search progress node + if (searchProgress) { + searchProgress.innerText = _("Preparing search..."); + } + Search.startPulse(); + + // index already loaded, the browser was quick! + if (Search.hasIndex()) Search.query(query); + else Search.deferQuery(query); + }, + + _parseQuery: (query) => { + // stem the search terms and add them to the correct list + const stemmer = new Stemmer(); + const searchTerms = new Set(); + const excludedTerms = new Set(); + const highlightTerms = new Set(); + const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); + splitQuery(query.trim()).forEach((queryTerm) => { + const queryTermLower = queryTerm.toLowerCase(); + + // maybe skip this "word" + // stopwords set is from language_data.js + if (stopwords.has(queryTermLower) || queryTerm.match(/^\d+$/)) return; + + // stem the word + let word = stemmer.stemWord(queryTermLower); + // select the correct list + if (word[0] === "-") excludedTerms.add(word.substr(1)); + else { + searchTerms.add(word); + highlightTerms.add(queryTermLower); + } + }); + + if (SPHINX_HIGHLIGHT_ENABLED) { + // SPHINX_HIGHLIGHT_ENABLED is set in sphinx_highlight.js + localStorage.setItem( + "sphinx_highlight_terms", + [...highlightTerms].join(" "), + ); + } + + // console.debug("SEARCH: searching for:"); + // console.info("required: ", [...searchTerms]); + // console.info("excluded: ", [...excludedTerms]); + + return [query, searchTerms, excludedTerms, highlightTerms, objectTerms]; + }, + + /** + * execute search (requires search index to be loaded) + */ + _performSearch: ( + query, + searchTerms, + excludedTerms, + highlightTerms, + objectTerms, + ) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + const allTitles = Search._index.alltitles; + const indexEntries = Search._index.indexentries; + + // Collect multiple result groups to be sorted separately and then ordered. + // Each is an array of [docname, title, anchor, descr, score, filename, kind]. + const normalResults = []; + const nonMainIndexResults = []; + + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase().trim(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if ( + title.toLowerCase().trim().includes(queryLower) + && queryLower.length >= title.length / 2 + ) { + for (const [file, id] of foundTitles) { + const score = Math.round( + (Scorer.title * queryLower.length) / title.length, + ); + const boost = titles[file] === title ? 1 : 0; // add a boost for document titles + normalResults.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score + boost, + filenames[file], + SearchResultKind.title, + ]); + } + } + } + + // search for explicit entries in index directives + for (const [entry, foundEntries] of Object.entries(indexEntries)) { + if (entry.includes(queryLower) && queryLower.length >= entry.length / 2) { + for (const [file, id, isMain] of foundEntries) { + const score = Math.round((100 * queryLower.length) / entry.length); + const result = [ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + SearchResultKind.index, + ]; + if (isMain) { + normalResults.push(result); + } else { + nonMainIndexResults.push(result); + } + } + } + } + + // lookup as object + objectTerms.forEach((term) => + normalResults.push(...Search.performObjectSearch(term, objectTerms)), + ); + + // lookup as search terms in fulltext + normalResults.push( + ...Search.performTermsSearch(searchTerms, excludedTerms), + ); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) { + normalResults.forEach((item) => (item[4] = Scorer.score(item))); + nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item))); + } + + // Sort each group of results by score and then alphabetically by name. + normalResults.sort(_orderResultsByScoreThenName); + nonMainIndexResults.sort(_orderResultsByScoreThenName); + + // Combine the result groups in (reverse) order. + // Non-main index entries are typically arbitrary cross-references, + // so display them after other results. + let results = [...nonMainIndexResults, ...normalResults]; + + // remove duplicate search results + // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept + let seen = new Set(); + results = results.reverse().reduce((acc, result) => { + let resultStr = result + .slice(0, 4) + .concat([result[5]]) + .map((v) => String(v)) + .join(","); + if (!seen.has(resultStr)) { + acc.push(result); + seen.add(resultStr); + } + return acc; + }, []); + + return results.reverse(); + }, + + query: (query) => { + const [ + searchQuery, + searchTerms, + excludedTerms, + highlightTerms, + objectTerms, + ] = Search._parseQuery(query); + const results = Search._performSearch( + searchQuery, + searchTerms, + excludedTerms, + highlightTerms, + objectTerms, + ); + + // for debugging + //Search.lastresults = results.slice(); // a copy + // console.info("search results:", Search.lastresults); + + // print the results + _displayNextItem(results, results.length, searchTerms, highlightTerms); + }, + + /** + * search for object names + */ + performObjectSearch: (object, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const objects = Search._index.objects; + const objNames = Search._index.objnames; + const titles = Search._index.titles; + + const results = []; + + const objectSearchCallback = (prefix, match) => { + const name = match[4]; + const fullname = (prefix ? prefix + "." : "") + name; + const fullnameLower = fullname.toLowerCase(); + if (fullnameLower.indexOf(object) < 0) return; + + let score = 0; + const parts = fullnameLower.split("."); + + // check for different match types: exact matches of full name or + // "last name" (i.e. last dotted part) + if (fullnameLower === object || parts.slice(-1)[0] === object) + score += Scorer.objNameMatch; + else if (parts.slice(-1)[0].indexOf(object) > -1) + score += Scorer.objPartialMatch; // matches in last name + + const objName = objNames[match[1]][2]; + const title = titles[match[0]]; + + // If more than one term searched for, we require other words to be + // found in the name/title/description + const otherTerms = new Set(objectTerms); + otherTerms.delete(object); + if (otherTerms.size > 0) { + const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); + if ( + [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) + ) + return; + } + + let anchor = match[3]; + if (anchor === "") anchor = fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + + const descr = objName + _(", in ") + title; + + // add custom score for some objects according to scorer + if (Scorer.objPrio.hasOwnProperty(match[2])) + score += Scorer.objPrio[match[2]]; + else score += Scorer.objPrioDefault; + + results.push([ + docNames[match[0]], + fullname, + "#" + anchor, + descr, + score, + filenames[match[0]], + SearchResultKind.object, + ]); + }; + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => objectSearchCallback(prefix, array)), + ); + return results; + }, + + /** + * search for full-text terms in the index + */ + performTermsSearch: (searchTerms, excludedTerms) => { + // prepare search + const terms = Search._index.terms; + const titleTerms = Search._index.titleterms; + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + + const scoreMap = new Map(); + const fileMap = new Map(); + + // perform the search on the required terms + searchTerms.forEach((word) => { + const files = []; + // find documents, if any, containing the query word in their text/title term indices + // use Object.hasOwnProperty to avoid mismatching against prototype properties + const arr = [ + { + files: terms.hasOwnProperty(word) ? terms[word] : undefined, + score: Scorer.term, + }, + { + files: titleTerms.hasOwnProperty(word) ? titleTerms[word] : undefined, + score: Scorer.title, + }, + ]; + // add support for partial matches + if (word.length > 2) { + const escapedWord = _escapeRegExp(word); + if (!terms.hasOwnProperty(word)) { + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + } + if (!titleTerms.hasOwnProperty(word)) { + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: titleTerms[term], score: Scorer.partialTitle }); + }); + } + } + + // no match but word was a required one + if (arr.every((record) => record.files === undefined)) return; + + // found search word in contents + arr.forEach((record) => { + if (record.files === undefined) return; + + let recordFiles = record.files; + if (recordFiles.length === undefined) recordFiles = [recordFiles]; + files.push(...recordFiles); + + // set score for the word in each file + recordFiles.forEach((file) => { + if (!scoreMap.has(file)) scoreMap.set(file, new Map()); + const fileScores = scoreMap.get(file); + fileScores.set(word, record.score); + }); + }); + + // create the mapping + files.forEach((file) => { + if (!fileMap.has(file)) fileMap.set(file, [word]); + else if (fileMap.get(file).indexOf(word) === -1) + fileMap.get(file).push(word); + }); + }); + + // now check if the files don't contain excluded terms + const results = []; + for (const [file, wordList] of fileMap) { + // check if all requirements are matched + + // as search terms with length < 3 are discarded + const filteredTermCount = [...searchTerms].filter( + (term) => term.length > 2, + ).length; + if ( + wordList.length !== searchTerms.size + && wordList.length !== filteredTermCount + ) + continue; + + // ensure that none of the excluded terms is in the search result + if ( + [...excludedTerms].some( + (term) => + terms[term] === file + || titleTerms[term] === file + || (terms[term] || []).includes(file) + || (titleTerms[term] || []).includes(file), + ) + ) + break; + + // select one (max) score for the file. + const score = Math.max(...wordList.map((w) => scoreMap.get(file).get(w))); + // add result to the result list + results.push([ + docNames[file], + titles[file], + "", + null, + score, + filenames[file], + SearchResultKind.text, + ]); + } + return results; + }, + + /** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words. + */ + makeSearchSummary: (htmlText, keywords, anchor) => { + const text = Search.htmlToText(htmlText, anchor); + if (text === "") return null; + + const textLower = text.toLowerCase(); + const actualStartPosition = [...keywords] + .map((k) => textLower.indexOf(k.toLowerCase())) + .filter((i) => i > -1) + .slice(-1)[0]; + const startWithContext = Math.max(actualStartPosition - 120, 0); + + const top = startWithContext === 0 ? "" : "..."; + const tail = startWithContext + 240 < text.length ? "..." : ""; + + let summary = document.createElement("p"); + summary.classList.add("context"); + summary.textContent = + top + text.substr(startWithContext, 240).trim() + tail; + + return summary; + }, +}; + +_ready(Search.init); diff --git a/src/scitex/_sphinx_html/_static/session_lifecycle.mmd b/src/scitex/_sphinx_html/_static/session_lifecycle.mmd new file mode 100644 index 000000000..d42ad8270 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/session_lifecycle.mmd @@ -0,0 +1,20 @@ +%%{init: {"theme": "default"}}%% +graph TD + A(["main()"]) + B["Parse CLI args from signature"] + C["Create session dir"] + D["Load config, inject globals"] + E["Execute function"] + F{"return code?"} + G(["FINISHED_SUCCESS/"]) + H(["FINISHED_ERROR/"]) + A --> B + B --> C + C --> D + D --> E + E --> F + F -->|"0"| G + F -->|"nonzero"| H + style E fill:#e8f4fd,stroke:#2196F3,stroke-width:2px + style G fill:#e8f5e9,stroke:#4CAF50 + style H fill:#fce4ec,stroke:#E91E63 diff --git a/src/scitex/_sphinx_html/_static/sphinx_highlight.js b/src/scitex/_sphinx_html/_static/sphinx_highlight.js new file mode 100644 index 000000000..a74e103a8 --- /dev/null +++ b/src/scitex/_sphinx_html/_static/sphinx_highlight.js @@ -0,0 +1,159 @@ +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true; + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 + && !parent.classList.contains(className) + && !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + const rest = document.createTextNode(val.substr(pos + text.length)); + parent.insertBefore(span, parent.insertBefore(rest, node.nextSibling)); + node.nodeValue = val.substr(0, pos); + /* There may be more occurrences of search term in this node. So call this + * function recursively on the remaining fragment. + */ + _highlight(rest, addItems, text, className); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect", + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target), + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms"); + // Update history only if '?highlight' is present; otherwise it + // clears text fragments (not set in window.location by the browser) + if (url.searchParams.has("highlight")) { + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + } + + // get individual terms from highlight string + const terms = highlight + .toLowerCase() + .split(/\s+/) + .filter((x) => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '", + ), + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms"); + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) + return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) + return; + if ( + DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + && event.key === "Escape" + ) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(() => { + /* Do not call highlightSearchWords() when we are on the search page. + * It will highlight words from the *previous* search query. + */ + if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); + SphinxHighlight.initEscapeListener(); +}); diff --git a/src/scitex/_sphinx_html/_static/workflow.mmd b/src/scitex/_sphinx_html/_static/workflow.mmd new file mode 100644 index 000000000..0be4d81ac --- /dev/null +++ b/src/scitex/_sphinx_html/_static/workflow.mmd @@ -0,0 +1,21 @@ +%%{init: {"theme": "default"}}%% +graph LR + A(["Research Question"]) + B["Literature
scholar"] + C["Data
io"] + D["Analysis
stats / dsp"] + E["Figures
plt"] + F["Manuscript
writer"] + G{"Verify
clew"} + H(["Publication"]) + + A --> B --> C --> D --> E --> F --> G --> H + + style A fill:#f5f5f5,stroke:#999 + style B fill:#f3e5f5,stroke:#9C27B0 + style C fill:#e8f4fd,stroke:#2196F3 + style D fill:#fff3e0,stroke:#FF9800 + style E fill:#fff3e0,stroke:#FF9800 + style F fill:#f3e5f5,stroke:#9C27B0 + style G fill:#f3e5f5,stroke:#9C27B0,stroke-width:2px + style H fill:#f5f5f5,stroke:#999 diff --git a/src/scitex/_sphinx_html/api/index.html b/src/scitex/_sphinx_html/api/index.html new file mode 100644 index 000000000..ae358dfac --- /dev/null +++ b/src/scitex/_sphinx_html/api/index.html @@ -0,0 +1,709 @@ + + + + + + + + + API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.ai.html b/src/scitex/_sphinx_html/api/scitex.ai.html new file mode 100644 index 000000000..3b0b126ab --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.ai.html @@ -0,0 +1,680 @@ + + + + + + + + + scitex.ai API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.ai API Reference

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.clew.html b/src/scitex/_sphinx_html/api/scitex.clew.html new file mode 100644 index 000000000..aa501c063 --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.clew.html @@ -0,0 +1,1200 @@ + + + + + + + + + scitex.clew API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.clew API Reference

+

scitex-clew — Hash-based verification for reproducible science.

+

Standalone package. Zero dependencies (pure stdlib + sqlite3). +When used with scitex, integration is automatic via @stx.session + stx.io.

+

Public API:

+
import scitex_clew as clew
+
+# Verification
+clew.status()                      # git-status-like overview
+clew.run(session_id)               # verify one run (hash check)
+clew.chain(target_file)            # trace file → source chain
+clew.dag(targets)                  # verify full DAG
+clew.rerun(target)                 # re-execute & compare (sandbox)
+clew.rerun_dag(targets)            # rerun full DAG in topo order
+clew.rerun_claims()                # rerun all claim-backing sessions
+clew.list_runs(limit=100)          # list tracked runs
+clew.stats()                       # database statistics
+
+# Claims
+clew.add_claim(...)                # register manuscript assertion
+clew.list_claims(...)              # list registered claims
+clew.verify_claim(...)             # verify a specific claim
+
+# Stamping
+clew.stamp(...)                    # create temporal proof
+clew.list_stamps(...)              # list stamps
+clew.check_stamp(...)              # verify a stamp
+
+# Hashing
+clew.hash_file(path)               # SHA256 of a file
+clew.hash_directory(path)          # SHA256 of all files in dir
+
+# Visualization
+clew.mermaid(...)                  # generate Mermaid DAG diagram
+
+# Examples
+clew.init_examples(dest)           # scaffold example pipeline
+
+# Session lifecycle hooks (invoked by @scitex.session)
+clew.on_session_start(session_id)  # open a tracked run
+clew.on_session_close(status=...)  # finalize run + combined hash
+
+
+
+
+scitex.clew.status()[source]
+

Get verification status summary (like git status).

+
+ +
+
+scitex.clew.run(session_id, from_scratch=False)[source]
+

Verify a specific run.

+
+
Parameters:
+
    +
  • session_id (str) – Session identifier

  • +
  • from_scratch (bool) – If True, re-execute the script and verify outputs (slow but thorough). +If False, only compare hashes (fast).

  • +
+
+
+
+ +
+
+scitex.clew.chain(target)[source]
+

Verify the dependency chain for a target file.

+
+ +
+
+scitex.clew.dag(targets=None, claims=False, strict=False)[source]
+

Verify the DAG for multiple targets or all claims.

+
+
Parameters:
+
    +
  • targets (list of str or Path, optional) – Target files to verify (mutually exclusive with claims).

  • +
  • claims (bool, optional) – If True, build the DAG from every registered claim.

  • +
  • strict (bool, optional) – If True (F2), return a failure-attribution dict with +failed_node / root_cause / invalidated_claims / +still_valid_claims instead of a DAGVerification.

  • +
+
+
+
+ +
+
+scitex.clew.rerun(target, timeout=300, cleanup=True)[source]
+

Re-execute a session in a sandbox and compare outputs.

+
+
Parameters:
+
    +
  • target (str or list[str]) – Session ID, script path, or artifact path.

  • +
  • timeout (int) – Maximum execution time in seconds (default: 300).

  • +
  • cleanup (bool) – Remove sandbox outputs after verification (default: True).

  • +
+
+
+
+ +
+
+scitex.clew.rerun_dag(targets=None, timeout=300, cleanup=True)[source]
+

Rerun-verify an entire DAG in topological order.

+

Each session is re-executed in a sandbox against its ORIGINAL stored +inputs (not freshly rerun outputs from upstream), then compared to +the original outputs.

+
+
Parameters:
+
    +
  • targets (list[str] | None) – Target output files whose upstream DAG should be rerun. +If None, all runs in the database are used and their output +files become the targets.

  • +
  • timeout (int) – Maximum execution time per session in seconds (default: 300).

  • +
  • cleanup (bool) – Whether to remove sandbox output directories after each rerun.

  • +
+
+
Returns:
+

Unified verification result for the entire DAG.

+
+
Return type:
+

DAGVerification

+
+
+
+ +
+
+scitex.clew.rerun_claims(file_path=None, claim_type=None, timeout=300, cleanup=True)[source]
+

Rerun-verify all sessions that produced files referenced by claims.

+

Collects unique source files from matching claims, then delegates +to rerun_dag with those files as targets.

+
+
Parameters:
+
    +
  • file_path (str | None) – Filter claims by manuscript file path.

  • +
  • claim_type (str | None) – Filter claims by type (statistic, figure, table, text, value).

  • +
  • timeout (int) – Maximum execution time per session in seconds (default: 300).

  • +
  • cleanup (bool) – Whether to remove sandbox output directories after each rerun.

  • +
+
+
Returns:
+

Unified verification result for the upstream DAG of all +source files referenced by the matching claims.

+
+
Return type:
+

DAGVerification

+
+
+
+ +
+
+scitex.clew.list_runs(limit=100, status=None)[source]
+

List tracked runs.

+
+ +
+
+scitex.clew.stats()[source]
+

Get database statistics.

+
+ +
+
+scitex.clew.add_claim(file_path, claim_type, line_number=None, claim_value=None, source_file=None, source_session=None)[source]
+

Register a claim linking a manuscript assertion to the verification chain.

+
+
Parameters:
+
    +
  • file_path (str) – Path to the manuscript file (e.g., paper.tex).

  • +
  • claim_type (str) – One of: statistic, figure, table, text, value.

  • +
  • line_number (Optional[int]) – Line number in the manuscript.

  • +
  • claim_value (Optional[str]) – The asserted value (e.g., “p = 0.003”).

  • +
  • source_file (Optional[str]) – Path to the source file that produced this claim.

  • +
  • source_session (Optional[str]) – Session ID that produced the source.

  • +
+
+
Returns:
+

The registered claim object.

+
+
Return type:
+

Claim

+
+
+
+ +
+
+scitex.clew.list_claims(file_path=None, claim_type=None, status=None, limit=100)[source]
+

List registered claims with optional filters.

+
+
Parameters:
+
    +
  • file_path (Optional[str]) – Filter by manuscript file path.

  • +
  • claim_type (Optional[str]) – Filter by claim type.

  • +
  • status (Optional[str]) – Filter by verification status.

  • +
  • limit (int) – Maximum number of claims to return.

  • +
+
+
Return type:
+

List[Claim]

+
+
+
+ +
+
+scitex.clew.verify_claim(claim_id_or_location)[source]
+

Verify a specific claim by checking its source against the verification chain.

+
+
Parameters:
+

claim_id_or_location (str) – Either a claim_id or a location string like “paper.tex:L42”.

+
+
Returns:
+

Verification result with claim details and chain status.

+
+
Return type:
+

Dict

+
+
+
+ +
+
+scitex.clew.export_claims_json(path=None, *, file_path_filter=None, read_only=True)[source]
+

Export every registered claim to a canonical JSON artifact.

+

The exported file is the single human-readable + machine-consumable +view of the claims table in db.sqlite. The DB remains the +source of truth; this JSON is a regenerable artifact.

+

Path resolution (mirrors scitex_clew._db._core._default_db_path()):

+
1. Explicit ``path`` argument.
+2. ``$SCITEX_CLEW_CLAIMS_JSON`` env var (escape hatch).
+3. ``<project_root>/.scitex/clew/runtime/claims.json``
+   (project root = nearest ancestor dir with ``.git`` or
+   ``pyproject.toml``; falls back to cwd if none found).
+
+
+
+
Parameters:
+
    +
  • path (Union[str, Path, None]) – Override the resolved path. Useful for tests / one-off dumps.

  • +
  • file_path_filter (Optional[str]) – When set, only claims registered against this manuscript file +path are exported. Default: every claim in the DB.

  • +
  • read_only (bool) – After writing, chmod 0o444 the file so accidental edits +fail loudly at the OS layer. Default True (the file IS +derived). Set False for tests that need to mutate the file.

  • +
+
+
Returns:
+

The path the artifact was written to (absolute).

+
+
Return type:
+

Path

+
+
+
+

Examples

+
>>> import scitex_clew as clew
+>>> clew.add_claim("paper.tex", "value", 42, "0.94", source_file="r.csv")
+>>> # claims.json now auto-exported under ./.scitex/clew/runtime/
+>>> clew.export_claims_json()  # idempotent — re-emit on demand
+PosixPath('.../.scitex/clew/runtime/claims.json')
+
+
+
+
+ +
+
+scitex.clew.register_intermediate(name, value, supports=None, session_id=None, claim_type='value')[source]
+

Register a computed intermediate as a Clew claim.

+

Use this from inside a @stx.session script (or from an agent loop) to +record any non-trivial intermediate value with explicit upstream support. +The claim becomes part of the DAG and can be queried via clew.chain, +clew.dag, or the MCP clew_chain / clew_dag tools.

+
+
Parameters:
+
    +
  • name (str) – Descriptive identifier (e.g. “acute_n_sig_pathways”). Avoid generic +names like “result_3” — the id is the only handle a future inspector +has on the value.

  • +
  • value (Any) – The computed result. Coerced to string for storage; the hash chain +sees repr(value) so types matter.

  • +
  • supports (Optional[List[str]]) – List of upstream claim ids or session ids that this value depends on. +Stored as JSON in the claim’s value field for retrieval. None means +no explicit upstream (use sparingly).

  • +
  • session_id (Optional[str]) – The session this value belongs to. If None, read from the +SCITEX_SESSION_ID env var that @stx.session sets at start.

  • +
  • claim_type (str) – One of statistic, figure, table, text, value. Defaults to +value since intermediates are usually scalar / categorical results.

  • +
+
+
Returns:
+

The registered claim object.

+
+
Return type:
+

Claim

+
+
Raises:
+

ValueError – If no session_id can be determined (env var unset and not passed).

+
+
+
+

Examples

+

Inside a @stx.session script:

+
>>> from scitex_clew import register_intermediate
+>>> n_sig = sum(1 for p in pathways if p.padj < 0.05)
+>>> register_intermediate(
+...     name="chronic_r2_n_sig_pathways",
+...     value=n_sig,
+...     supports=["chronic_r2_min_pvals", "reactome_pathways_v2024"],
+... )
+
+
+
+
+ +
+
+scitex.clew.stamp(backend='file', service_url=None, session_ids=None, output_dir=None)[source]
+

Record root hash with external timestamp.

+
+
Parameters:
+
    +
  • backend (str) – One of: file, rfc3161, zenodo.

  • +
  • service_url (Optional[str]) – URL for RFC 3161 TSA or Zenodo API.

  • +
  • session_ids (Optional[List[str]]) – Specific sessions to stamp. If None, stamps all successful runs.

  • +
  • output_dir (Optional[str]) – Directory for file-based stamps (default: <db_dir>/stamps, i.e. .scitex/clew/runtime/stamps/).

  • +
+
+
Returns:
+

The timestamp proof record.

+
+
Return type:
+

Stamp

+
+
+
+ +
+
+scitex.clew.list_stamps(limit=20)[source]
+

List all stamps.

+
+
Return type:
+

List[Stamp]

+
+
+
+ +
+
+scitex.clew.check_stamp(stamp_id=None)[source]
+

Verify a stamp against current verification state.

+
+
Parameters:
+

stamp_id (Optional[str]) – Specific stamp to check. If None, checks the latest stamp.

+
+
Returns:
+

{stamp, current_root_hash, matches, details}

+
+
Return type:
+

Dict

+
+
+
+ +
+
+scitex.clew.hash_file(path, algorithm='sha256', chunk_size=8192)[source]
+

Compute hash of a file.

+
+
Parameters:
+
    +
  • path (Union[str, Path]) – Path to the file to hash

  • +
  • algorithm (str) – Hash algorithm (default: sha256)

  • +
  • chunk_size (int) – Size of chunks to read (default: 8192)

  • +
+
+
Returns:
+

Hexadecimal hash string (first 32 characters)

+
+
Return type:
+

str

+
+
+
+

Examples

+
>>> hash_file("data.csv")
+'a1b2c3d4e5f6...'
+
+
+
+
+ +
+
+scitex.clew.hash_directory(path, pattern='*', recursive=True, algorithm='sha256')[source]
+

Compute hashes for all files in a directory.

+
+
Parameters:
+
    +
  • path (Union[str, Path]) – Directory path

  • +
  • pattern (str) – Glob pattern for files (default: “*”)

  • +
  • recursive (bool) – Whether to search recursively (default: True)

  • +
  • algorithm (str) – Hash algorithm (default: sha256)

  • +
+
+
Returns:
+

Mapping of relative paths to hashes

+
+
Return type:
+

Dict[str, str]

+
+
+
+

Examples

+
>>> hash_directory("./data/")
+{'input.csv': 'a1b2...', 'config.yaml': 'c3d4...'}
+
+
+
+
+ +
+
+scitex.clew.mermaid(session_id=None, target_file=None, target_files=None, claims=False, grouper=None, **kwargs)[source]
+

Generate a Mermaid DAG diagram.

+
+
Parameters:
+
    +
  • session_id (str, optional) – Start from this session.

  • +
  • target_file (str, optional) – Start from the session that produced this file.

  • +
  • target_files (list of str, optional) – Multiple target files (multi-target DAG).

  • +
  • claims (bool, optional) – If True, build DAG from all registered claims.

  • +
  • grouper (callable | dict | None, optional) – File grouping strategy. Callable or JSON/dict spec (see +scitex_clew.groupers.resolve_spec). If None, falls back to +.scitex/clew/config.yaml (key grouper) if present.

  • +
+
+
+
+ +
+
+scitex.clew.init_examples(dest, variant='sequential', *, find_examples_dir=<function _find_examples_dir>)[source]
+

Copy Clew example scripts to a destination directory.

+

Copies only the runnable scripts (.py, .sh) and README — not +the output directories. Users run 00_run_all.sh themselves +to generate outputs and populate the verification database.

+
+
Parameters:
+
    +
  • dest (str | Path) – Destination directory. Created if it does not exist. +Existing script files are overwritten.

  • +
  • variant (str) – Example variant: “sequential” (default) or “multi_parent”.

  • +
  • find_examples_dir (callable, optional) – Locator callable (variant: str) -> Optional[Path] used to +resolve the bundled examples source. Production callers should +not pass this; it is the canonical PA-306 §1 DI seam — tests +inject a hand-rolled fake that returns a tmp_path-rooted +directory or None.

  • +
+
+
Returns:
+

{"path": str, "files": list[str], "file_count": int, "variant": str}

+
+
Return type:
+

dict

+
+
Raises:
+
+
+
+
+ +
+
+scitex.clew.on_session_start(session_id, script_path=None, parent_session=None, verbose=False, metadata=None)[source]
+

Hook called when a session starts.

+
+
Parameters:
+
    +
  • session_id (str) – Unique session identifier

  • +
  • script_path (Optional[str]) – Path to the script being run

  • +
  • parent_session (Optional[str]) – Parent session ID for chain tracking

  • +
  • verbose (bool) – Whether to log status messages

  • +
  • metadata (Optional[dict]) – Additional metadata (e.g. notebook_path, cell_index)

  • +
+
+
Return type:
+

None

+
+
+
+ +
+
+scitex.clew.on_session_close(status='success', exit_code=0, verbose=False, register=None)[source]
+

Hook called when a session closes.

+
+
Parameters:
+
    +
  • status (str) – Final status (success, failed, error)

  • +
  • exit_code (int) – Exit code of the script

  • +
  • verbose (bool) – Whether to log status messages

  • +
  • register (Optional[bool]) – If True, register session hashes with remote Clew Registry. +If None, checks SCITEX_AUTO_REGISTER environment variable.

  • +
+
+
Return type:
+

None

+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.config.html b/src/scitex/_sphinx_html/api/scitex.config.html new file mode 100644 index 000000000..a4cb6ec0f --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.config.html @@ -0,0 +1,1330 @@ + + + + + + + + + scitex.config API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.config API Reference

+

scitex-config — configuration helpers (YAML + dotenv) — standalone.

+

Two distinct API surfaces:

+

Public (top-level scitex_config.*) — convention-free generic +primitives usable by any Python project:

+
    +
  • PriorityConfig — direct → config_dict → env → default cascade

  • +
  • ScitexConfig, get_config, load_yaml — YAML-based config

  • +
  • ScitexPaths, get_paths — centralized path manager

  • +
  • load_dotenv, get_scitex_dir — utilities

  • +
  • parse_src_file, load_env_from_path, load_scitex_env — bash-style +.src/.env parsing (canonical line/value parser; load_dotenv delegates)

  • +
+

SciTeX-ecosystem internals (scitex_config._ecosystem.*) — helpers +that embed SciTeX conventions (pkg-short naming, project-scope walk to +.git/, _skills/<pkg>/ layout, SCITEX_<MODULE>_* env prefix). +For scitex-* package authors only; not a stable public API:

+
from scitex_config._ecosystem import local_state, env_registry
+
+
+
+
Priority Order (same for PriorityConfig and ScitexConfig):

direct → config (YAML/dict) → env → default

+
+
Usage:

from scitex_config import ScitexConfig, ScitexPaths, get_config, get_paths

+

# YAML-based configuration (Scholar pattern) +config = get_config() +log_level = config.resolve(“logging.level”, default=”INFO”)

+

# Centralized path manager +paths = get_paths() +print(paths.logs) # ~/.scitex/logs +print(paths.cache) # ~/.scitex/cache

+

# Use resolve() pattern in modules +cache_dir = paths.resolve(“cache”, user_provided_path)

+
+
+
+
+class scitex.config.ScitexConfig(config_path=None, env_prefix='SCITEX_')[source]
+

Bases: object

+

YAML-based configuration manager for SciTeX.

+

Loads configuration from YAML files with environment variable substitution. +Values can be resolved with priority: direct → config → env → default.

+
+

Examples

+
>>> from scitex.config import ScitexConfig
+>>> config = ScitexConfig()
+>>> config.resolve("logging.level", default="INFO")
+'INFO'
+>>> config.get("debug.enabled")
+False
+
+
+
+
+
+__init__(config_path=None, env_prefix='SCITEX_')[source]
+

Initialize ScitexConfig.

+
+
Parameters:
+
    +
  • config_path (Union[str, Path, None]) – Path to custom YAML config file. If None, uses default.yaml.

  • +
  • env_prefix (str) – Prefix for environment variables (default: “SCITEX_”)

  • +
+
+
+
+ +
+
+get(key, default=None)[source]
+

Get value from config directly (no precedence resolution).

+

Supports dot notation for nested keys.

+
+
Parameters:
+
    +
  • key (str) – Configuration key (e.g., “logging.level” or “debug.enabled”)

  • +
  • default (Any) – Default value if key not found

  • +
+
+
Returns:
+

Configuration value

+
+
Return type:
+

Any

+
+
+
+ +
+
+resolve(key, direct_val=None, default=None, type=<class 'str'>)[source]
+

Resolve value with precedence: direct → config → env → default.

+

This follows the Scholar module’s CascadeConfig pattern where +YAML config takes higher priority than environment variables.

+
+
Parameters:
+
    +
  • key (str) – Configuration key (e.g., “logging.level”)

  • +
  • direct_val (Any) – Direct value (highest precedence)

  • +
  • default (Any) – Default value (lowest precedence)

  • +
  • type (Type) – Type conversion (str, int, float, bool, list)

  • +
+
+
Returns:
+

Resolved value

+
+
Return type:
+

Any

+
+
+
+ +
+
+get_nested(*keys, default=None)[source]
+

Get nested value from original config structure.

+
+
Parameters:
+
    +
  • *keys (str) – Keys to traverse (e.g., “browser”, “screenshots_dir”)

  • +
  • default (Any) – Default value if not found

  • +
+
+
Returns:
+

Nested value

+
+
Return type:
+

Any

+
+
+
+ +
+
+property config_path: Path
+

Get the path to the loaded config file.

+
+ +
+
+property raw: dict
+

Get raw configuration data (original nested structure).

+
+ +
+
+property flat: dict
+

Get flattened configuration data.

+
+ +
+
+print()[source]
+

Print configuration resolution log.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+scitex.config.get_config(config_path=None)[source]
+

Get ScitexConfig instance.

+
+
Parameters:
+

config_path (Union[str, Path, None]) – Path to custom config. If None, returns cached default instance.

+
+
Returns:
+

Configuration instance

+
+
Return type:
+

ScitexConfig

+
+
+
+ +
+
+scitex.config.load_yaml(path)[source]
+

Load YAML file with environment variable substitution.

+

Supports ${VAR:-default} syntax for environment variable expansion.

+
+
Parameters:
+

path (Path) – Path to YAML file

+
+
Returns:
+

Parsed YAML with environment variables substituted

+
+
Return type:
+

dict

+
+
+
+ +
+
+class scitex.config.ScitexPaths(base_dir=None)[source]
+

Bases: object

+

Centralized path manager for SciTeX directories.

+

All paths are derived from SCITEX_DIR (default: ~/.scitex). +Priority: direct_val → SCITEX_DIR env → .env file → default

+
+
Directory Structure:

$SCITEX_DIR/ +├── browser/ # Browser profiles and data +│ ├── screenshots/ # Browser debugging screenshots +│ ├── sessions/ # Shared browser sessions +│ └── persistent/ # Persistent browser profiles +├── cache/ # General cache +│ └── functions/ # Function cache (joblib) +├── capture/ # Screen captures +├── impact_factor_cache/ # Impact factor data cache +├── logs/ # Log files +├── openathens_cache/ # OpenAthens auth cache +├── rng/ # Random number generator state +├── scholar/ # Scholar module data +│ ├── cache/ # Scholar-specific cache +│ └── library/ # PDF library +├── screenshots/ # General screenshots +├── test_monitor/ # Test monitoring screenshots +└── writer/ # Writer module data

+
+
+
+
+__init__(base_dir=None)[source]
+

Initialize ScitexPaths.

+
+
Parameters:
+

base_dir (Optional[str]) – Explicit base directory. If None, uses SCITEX_DIR env var +or falls back to ~/.scitex.

+
+
+
+ +
+
+property base: Path
+

Base SciTeX directory ($SCITEX_DIR or ~/.scitex).

+
+ +
+
+property logs: Path
+

Log files directory.

+
+ +
+
+property cache: Path
+

General cache directory.

+
+ +
+
+property capture: Path
+

Screen capture directory.

+
+ +
+
+property screenshots: Path
+

General screenshots directory.

+
+ +
+
+property rng: Path
+

Random number generator state directory.

+
+ +
+
+property browser: Path
+

Browser module base directory.

+
+ +
+
+property browser_screenshots: Path
+

Browser debugging screenshots.

+
+ +
+
+property browser_sessions: Path
+

Shared browser sessions.

+
+ +
+
+property browser_persistent: Path
+

Persistent browser profiles.

+
+ +
+
+property test_monitor: Path
+

Test monitoring screenshots directory.

+
+ +
+
+property function_cache: Path
+

Function cache (joblib memory).

+
+ +
+
+property impact_factor_cache: Path
+

Impact factor data cache.

+
+ +
+
+property openathens_cache: Path
+

OpenAthens authentication cache.

+
+ +
+
+property scholar: Path
+

Scholar module base directory.

+
+ +
+
+property scholar_cache: Path
+

Scholar-specific cache directory.

+
+ +
+
+property scholar_library: Path
+

Scholar PDF library directory.

+
+ +
+
+property writer: Path
+

Writer module directory.

+
+ +
+
+resolve(path_name, direct_val=None)[source]
+

Resolve a path with priority: direct_val → default from SCITEX_DIR.

+

This is the recommended method for modules that accept optional path +parameters. It follows the same pattern as PriorityConfig.resolve().

+
+
Parameters:
+
    +
  • path_name (str) – Name of the path property (e.g., “cache”, “logs”, “scholar_library”)

  • +
  • direct_val (Union[str, Path, None]) – Direct value (highest precedence). If None, uses default.

  • +
+
+
Returns:
+

Resolved path

+
+
Return type:
+

Path

+
+
+
+

Examples

+
>>> paths = ScitexPaths()
+>>> # User didn't provide path -> use default
+>>> cache_dir = paths.resolve("cache", None)
+>>> # User provided custom path -> use it
+>>> cache_dir = paths.resolve("cache", "/custom/cache")
+
+
+

Usage in modules: +>>> class MyModule: +… def __init__(self, cache_dir=None): +… self.cache_dir = get_paths().resolve(“cache”, cache_dir)

+
+
+ +
+
+ensure_dir(path)[source]
+

Ensure directory exists, creating if necessary.

+
+
Parameters:
+

path (Path) – Directory path to ensure exists.

+
+
Returns:
+

The same path, guaranteed to exist.

+
+
Return type:
+

Path

+
+
+
+ +
+
+ensure_all()[source]
+

Create all standard directories.

+
+
Return type:
+

None

+
+
+
+ +
+
+list_all()[source]
+

List all configured paths.

+
+
Returns:
+

Dictionary of path names to Path objects.

+
+
Return type:
+

dict

+
+
+
+ +
+ +
+
+scitex.config.get_paths(base_dir=None)[source]
+

Get ScitexPaths instance.

+
+
Parameters:
+

base_dir (Optional[str]) – Explicit base directory. If None, returns cached default instance.

+
+
Returns:
+

Path manager instance.

+
+
Return type:
+

ScitexPaths

+
+
+
+ +
+
+class scitex.config.PriorityConfig(config_dict=None, env_prefix='', auto_uppercase=True)[source]
+

Bases: object

+

Universal config resolver with precedence: direct → config_dict → env → default

+

Config dict (from YAML or passed dict) takes priority over env variables. +This follows the Scholar module’s CascadeConfig pattern.

+
+

Examples

+
>>> from scitex_config import PriorityConfig
+>>> config = PriorityConfig(config_dict={"port": 3000}, env_prefix="SCITEX_")
+>>> port = config.resolve("port", None, default=8000, type=int)
+3000  # from config_dict (highest after direct)
+>>> # With env: SCITEX_PORT=5000 python script.py
+>>> port = config.resolve("port", None, default=8000, type=int)
+3000  # config_dict takes precedence over env
+>>> port = config.resolve("port", 9000, default=8000, type=int)
+9000  # direct value takes highest precedence
+
+
+
+
+
+SENSITIVE_EXPRESSIONS = ['API', 'PASSWORD', 'SECRET', 'TOKEN', 'KEY', 'PASS', 'AUTH', 'CREDENTIAL', 'PRIVATE', 'CERT']
+
+ +
+
+__init__(config_dict=None, env_prefix='', auto_uppercase=True)[source]
+

Initialize PriorityConfig.

+
+
Parameters:
+
    +
  • config_dict (Optional[Dict[str, Any]]) – Dictionary with configuration values

  • +
  • env_prefix (str) – Prefix for environment variables (e.g., “SCITEX_”)

  • +
  • auto_uppercase (bool) – Whether to uppercase keys for env lookup

  • +
+
+
+
+ +
+
+get(key)[source]
+

Get value from config dict only.

+
+
Return type:
+

Any

+
+
+
+ +
+
+resolve(key, direct_val=None, default=None, type=<class 'str'>, mask=None)[source]
+

Get value with precedence hierarchy.

+

Precedence: direct → config_dict → env → default

+

This follows the Scholar module’s CascadeConfig pattern where +config dict takes higher priority than environment variables.

+
+
Parameters:
+
    +
  • key (str) – Configuration key to resolve

  • +
  • direct_val (Any) – Direct value (highest precedence)

  • +
  • default (Any) – Default value if not found elsewhere

  • +
  • type (Type) – Type conversion (str, int, float, bool, list)

  • +
  • mask (Optional[bool]) – Override automatic masking of sensitive values

  • +
+
+
Returns:
+

Resolved configuration value

+
+
Return type:
+

Any

+
+
+
+ +
+
+print_resolutions()[source]
+

Print how each config was resolved.

+
+
Return type:
+

None

+
+
+
+ +
+
+clear_log()[source]
+

Clear resolution log.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+scitex.config.get_scitex_dir(direct_val=None)[source]
+

Get SCITEX_DIR with priority: direct → env → default.

+

This is a convenience function for the most common use case.

+
+
Parameters:
+

direct_val (Optional[str]) – Direct value (highest precedence)

+
+
Returns:
+

Resolved SCITEX_DIR path

+
+
Return type:
+

Path

+
+
+
+ +
+
+scitex.config.load_dotenv(dotenv_path=None, *, walk_up=False, stop_at=None)[source]
+

Load environment variables from .env file(s).

+
+
Default behavior (walk_up=False, backward compatible):

Searches for .env file in the following order, loading the first match: +1. Explicit dotenv_path if provided +2. Current working directory (cwd/.env) +3. User home directory ($HOME/.env)

+
+
Parent-walking behavior (walk_up=True, opt-in):

Walks parent directories starting from cwd, looking for .env +at each level. Stops when reaching stop_at (or $HOME if not +given) or the filesystem root. All .env files found are loaded, +with the most-distant parent loaded first so that closer-to-cwd values +take precedence (closer .env wins). An existing process env var is +never overridden by any .env (process env > closest .env > … > root .env).

+

Note: walk_up=True is ignored if dotenv_path is explicitly given.

+
+
+
+
Parameters:
+
    +
  • dotenv_path (Optional[str]) – Path to .env file. If None, searches default locations.

  • +
  • walk_up (bool) – If True (and dotenv_path not given), walk parent dirs from cwd. +Default False for backward compatibility — new callers should pass True.

  • +
  • stop_at (Union[str, Path, None]) – Directory at which to stop the upward walk (inclusive — its .env +is considered). If None, stops at $HOME (or filesystem root if +$HOME is not a parent of cwd). Only used when walk_up=True.

  • +
+
+
Returns:
+

True if at least one .env file was found and loaded, False otherwise.

+
+
Return type:
+

bool

+
+
+
+ +
+
+scitex.config.parse_src_file(filepath)[source]
+

Parse a bash-compatible .src/.env file and extract env variables.

+
+
Parameters:
+

filepath (Path) – Path to the file.

+
+
Returns:
+

Dictionary of variable names to values.

+
+
Return type:
+

Dict[str, str]

+
+
+
+ +
+
+scitex.config.load_env_from_path(path)[source]
+

Load environment variables from a file or directory.

+
+
Parameters:
+

path (str) – Path to a .src file or directory containing *.src files.

+
+
Returns:
+

All loaded environment variables.

+
+
Return type:
+

Dict[str, str]

+
+
+
+ +
+
+scitex.config.load_scitex_env()[source]
+

Load environment variables from $SCITEX_ENV_SRC if set.

+

This function should be called early in the MCP server startup.

+
+
Returns:
+

Number of environment variables loaded.

+
+
Return type:
+

int

+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.db.html b/src/scitex/_sphinx_html/api/scitex.db.html new file mode 100644 index 000000000..1589ada33 --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.db.html @@ -0,0 +1,680 @@ + + + + + + + + + scitex.db API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.db API Reference

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.decorators.html b/src/scitex/_sphinx_html/api/scitex.decorators.html new file mode 100644 index 000000000..2bff16939 --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.decorators.html @@ -0,0 +1,680 @@ + + + + + + + + + scitex.decorators API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.decorators API Reference

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.diagram.html b/src/scitex/_sphinx_html/api/scitex.diagram.html new file mode 100644 index 000000000..f397a42a9 --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.diagram.html @@ -0,0 +1,680 @@ + + + + + + + + + scitex.diagram API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.diagram API Reference

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.dict.html b/src/scitex/_sphinx_html/api/scitex.dict.html new file mode 100644 index 000000000..71d8fc5b3 --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.dict.html @@ -0,0 +1,680 @@ + + + + + + + + + scitex.dict API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.dict API Reference

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.dsp.html b/src/scitex/_sphinx_html/api/scitex.dsp.html new file mode 100644 index 000000000..2fd42aa3f --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.dsp.html @@ -0,0 +1,680 @@ + + + + + + + + + scitex.dsp API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.dsp API Reference

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.gen.html b/src/scitex/_sphinx_html/api/scitex.gen.html new file mode 100644 index 000000000..9e1d48030 --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.gen.html @@ -0,0 +1,680 @@ + + + + + + + + + scitex.gen API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.gen API Reference

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.introspect.html b/src/scitex/_sphinx_html/api/scitex.introspect.html new file mode 100644 index 000000000..a0c917cc2 --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.introspect.html @@ -0,0 +1,680 @@ + + + + + + + + + scitex.introspect API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.introspect API Reference

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.io.html b/src/scitex/_sphinx_html/api/scitex.io.html new file mode 100644 index 000000000..2662c8502 --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.io.html @@ -0,0 +1,680 @@ + + + + + + + + + scitex.io API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.io API Reference

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.linter.html b/src/scitex/_sphinx_html/api/scitex.linter.html new file mode 100644 index 000000000..bad5299ec --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.linter.html @@ -0,0 +1,680 @@ + + + + + + + + + scitex.linter — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.linter

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.logging.html b/src/scitex/_sphinx_html/api/scitex.logging.html new file mode 100644 index 000000000..907cfe2b9 --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.logging.html @@ -0,0 +1,1272 @@ + + + + + + + + + scitex.logging API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.logging API Reference

+
+
+scitex.logging.getLogger(name=None)[source]
+

Return a logger with the specified name, creating it if necessary.

+

If no name is specified, return the root logger.

+
+ +
+
+scitex.logging.configure(level='info', log_file=None, enable_file=True, enable_console=True, capture_prints=True, max_file_size=10485760, backup_count=5)[source]
+

Configure logging for SciTeX with both console and file output.

+
+
Parameters:
+
    +
  • level (Union[str, int]) – Log level (string or logging constant)

  • +
  • log_file (Optional[str]) – Path to log file (default: ~/.scitex/logs/scitex-YYYY-MM-DD.log)

  • +
  • enable_file (bool) – Whether to enable file logging

  • +
  • enable_console (bool) – Whether to enable console logging

  • +
  • capture_prints (bool) – Whether to capture print() statements to logs

  • +
  • max_file_size (int) – Maximum size of log file before rotation (default: 10MB)

  • +
  • backup_count (int) – Number of backup files to keep (default: 5)

  • +
+
+
+
+ +
+
+scitex.logging.set_level(level)[source]
+

Set global log level for all SciTeX loggers.

+
+ +
+
+scitex.logging.get_level()[source]
+

Get current global log level.

+
+ +
+
+scitex.logging.enable_file_logging(enabled=True)[source]
+

Enable or disable file logging globally.

+
+ +
+
+scitex.logging.is_file_logging_enabled()[source]
+

Check if file logging is enabled.

+
+ +
+
+scitex.logging.get_log_path()[source]
+

Get the current log file path.

+
+ +
+
+class scitex.logging.Tee(stream, log_path, verbose=True)[source]
+

Bases: object

+
+
+write(data)[source]
+
+
Return type:
+

None

+
+
+
+ +
+
+flush()[source]
+
+
Return type:
+

None

+
+
+
+ +
+
+isatty()[source]
+
+
Return type:
+

bool

+
+
+
+ +
+
+fileno()[source]
+
+
Return type:
+

int

+
+
+
+ +
+
+property buffer
+
+ +
+
+close()[source]
+

Explicitly close the log file.

+
+ +
+ +
+
+scitex.logging.tee(sys, sdir=None, verbose=True)[source]
+

Redirects stdout and stderr to both console and log files.

+
+

Example

+
>>> import sys
+>>> sys.stdout, sys.stderr = tee(sys)
+>>> print("abc")  # stdout
+>>> print(1 / 0)  # stderr
+
+
+
+
+
Parameters:
+
    +
  • sys_module (module) – System module containing stdout and stderr

  • +
  • sdir (str, optional) – Directory for log files

  • +
  • verbose (bool, default=True) – Whether to print log file locations

  • +
+
+
Returns:
+

Wrapped stdout and stderr objects

+
+
Return type:
+

tuple[Any, Any]

+
+
+
+ +
+
+scitex.logging.log_to_file(file_path, level=10, mode='w', formatter=None)[source]
+

Context manager to temporarily log all output to a specific file.

+
+
Usage:

import scitex_logging as logging +logger = logging.getLogger(__name__)

+
+
with logging.log_to_file(“/path/to/log.txt”):

logger.info(“This goes to both console and /path/to/log.txt”) +logger.success(“This too!”)

+
+
+
+
+
+
Parameters:
+
    +
  • file_path (Union[str, Path]) – Path to log file

  • +
  • level (int) – Logging level for this handler (default: DEBUG)

  • +
  • mode (str) – File mode (‘w’ for overwrite, ‘a’ for append)

  • +
  • formatter (Optional[Formatter]) – Custom formatter (default: SciTeXFileFormatter)

  • +
+
+
Yields:
+

The file handler (can be ignored)

+
+
+
+ +
+
+exception scitex.logging.SciTeXWarning[source]
+

Bases: UserWarning

+

Base warning class for all SciTeX warnings.

+
+ +
+
+exception scitex.logging.UnitWarning[source]
+

Bases: SciTeXWarning

+

Warning for axis label unit issues (educational for SI conventions).

+

Raised when: +- Axis labels are missing units +- Units use parentheses instead of brackets (SI prefers []) +- Units use division instead of negative exponents (m/s vs m·s⁻¹)

+
+ +
+
+exception scitex.logging.StyleWarning[source]
+

Bases: SciTeXWarning

+

Warning for style/formatting issues.

+
+ +
+
+exception scitex.logging.SciTeXDeprecationWarning[source]
+

Bases: SciTeXWarning

+

Warning for deprecated SciTeX features.

+
+ +
+
+exception scitex.logging.PerformanceWarning[source]
+

Bases: SciTeXWarning

+

Warning for performance issues.

+
+ +
+
+exception scitex.logging.DataLossWarning[source]
+

Bases: SciTeXWarning

+

Warning for potential data loss.

+
+ +
+
+scitex.logging.warn(message, category=<class 'scitex_logging._warnings.SciTeXWarning'>, stacklevel=2)[source]
+

Emit a warning (like warnings.warn but integrated with scitex.logging).

+
+
Parameters:
+
    +
  • message (str) – Warning message

  • +
  • category (Type[SciTeXWarning]) – Warning category (default: SciTeXWarning)

  • +
  • stacklevel (int) – Stack level for source location (default: 2 = caller)

  • +
+
+
Return type:
+

None

+
+
+
+

Examples

+
>>> import scitex.logging as logging
+>>> from scitex.logging import UnitWarning
+>>> logging.warn("X axis has no units", UnitWarning)
+
+
+
+
+ +
+
+scitex.logging.filterwarnings(action, category=<class 'scitex_logging._warnings.SciTeXWarning'>, message=None)[source]
+

Control warning behavior (like warnings.filterwarnings).

+
+
Parameters:
+
    +
  • action (str) – One of: +- “ignore”: Never show this warning +- “error”: Raise as exception +- “always”: Always show +- “default”: Show first occurrence per location +- “once”: Show only once total +- “module”: Show once per module

  • +
  • category (Type[SciTeXWarning]) – Warning category (default: SciTeXWarning = all)

  • +
  • message (Optional[str]) – Regex pattern to match warning message (not implemented yet)

  • +
+
+
Return type:
+

None

+
+
+
+

Examples

+
>>> import scitex.logging as logging
+>>> from scitex.logging import UnitWarning
+>>> logging.filterwarnings("ignore", category=UnitWarning)
+
+
+
+
+ +
+
+scitex.logging.resetwarnings()[source]
+

Reset all warning filters to default behavior.

+
+
Return type:
+

None

+
+
+
+ +
+
+scitex.logging.warn_deprecated(old_name, new_name, version=None)[source]
+

Issue a deprecation warning.

+
+
Return type:
+

None

+
+
+
+ +
+
+scitex.logging.warn_performance(operation, suggestion)[source]
+

Issue a performance warning.

+
+
Return type:
+

None

+
+
+
+ +
+
+scitex.logging.warn_data_loss(operation, detail)[source]
+

Issue a data loss warning.

+
+
Return type:
+

None

+
+
+
+ +
+
+exception scitex.logging.SciTeXError(message, context=None, suggestion=None)[source]
+

Bases: Exception

+

Base Exception class for all SciTeX errors.

+
+
+__init__(message, context=None, suggestion=None)[source]
+

Initialize SciTeX error with detailed information.

+
+
Parameters:
+
    +
  • message (str) – The error message

  • +
  • context (Optional[dict]) – Additional context information (e.g., file paths, variable values)

  • +
  • suggestion (Optional[str]) – Suggested fix or action

  • +
+
+
+
+ +
+ +
+
+exception scitex.logging.ConfigurationError(message, context=None, suggestion=None)[source]
+

Bases: SciTeXError

+

Raised when there are issues with SciTeX configuration.

+
+ +
+
+exception scitex.logging.ConfigFileNotFoundError(filepath)[source]
+

Bases: ConfigurationError

+

Raised when a required configuration file is not found.

+
+ +
+
+exception scitex.logging.ConfigKeyError(key, available_keys=None)[source]
+

Bases: ConfigurationError

+

Raised when a required configuration key is missing.

+
+ +
+
+exception scitex.logging.IOError(message, context=None, suggestion=None)[source]
+

Bases: SciTeXError

+

Base class for input/output related errors.

+
+ +
+
+exception scitex.logging.FileFormatError(filepath, expected_format=None, actual_format=None)[source]
+

Bases: IOError

+

Raised when file format is not supported or incorrect.

+
+ +
+
+exception scitex.logging.SaveError(filepath, reason)[source]
+

Bases: IOError

+

Raised when saving data fails.

+
+ +
+
+exception scitex.logging.LoadError(filepath, reason)[source]
+

Bases: IOError

+

Raised when loading data fails.

+
+ +
+
+exception scitex.logging.ScholarError(message, context=None, suggestion=None)[source]
+

Bases: SciTeXError

+

Base class for scholar module errors.

+
+ +
+
+exception scitex.logging.SearchError(query, source, reason)[source]
+

Bases: ScholarError

+

Raised when paper search fails.

+
+ +
+
+exception scitex.logging.EnrichmentError(paper_title, reason)[source]
+

Bases: ScholarError

+

Raised when paper enrichment fails.

+
+ +
+
+exception scitex.logging.PDFDownloadError(url, reason)[source]
+

Bases: ScholarError

+

Raised when PDF download fails.

+
+ +
+
+exception scitex.logging.DOIResolutionError(doi, reason)[source]
+

Bases: ScholarError

+

Raised when DOI resolution fails.

+
+ +
+
+exception scitex.logging.PDFExtractionError(filepath, reason)[source]
+

Bases: ScholarError

+

Raised when PDF text extraction fails.

+
+ +
+
+exception scitex.logging.BibTeXEnrichmentError(bibtex_file, reason)[source]
+

Bases: ScholarError

+

Raised when BibTeX enrichment fails.

+
+ +
+
+exception scitex.logging.TranslatorError(translator_name, reason)[source]
+

Bases: ScholarError

+

Raised when Zotero translator operations fail.

+
+ +
+
+exception scitex.logging.AuthenticationError(provider, reason='')[source]
+

Bases: ScholarError

+

Raised when authentication fails.

+
+ +
+
+exception scitex.logging.PlottingError(message, context=None, suggestion=None)[source]
+

Bases: SciTeXError

+

Base class for plotting-related errors.

+
+ +
+
+exception scitex.logging.FigureNotFoundError(fig_id)[source]
+

Bases: PlottingError

+

Raised when attempting to operate on a non-existent figure.

+
+ +
+
+exception scitex.logging.AxisError(message, axis_info=None)[source]
+

Bases: PlottingError

+

Raised when there are issues with plot axes.

+
+ +
+
+exception scitex.logging.DataError(message, context=None, suggestion=None)[source]
+

Bases: SciTeXError

+

Base class for data processing errors.

+
+ +
+
+exception scitex.logging.ShapeError(expected_shape, actual_shape, operation)[source]
+

Bases: DataError

+

Raised when data shapes are incompatible.

+
+ +
+
+exception scitex.logging.DTypeError(expected_dtype, actual_dtype, operation)[source]
+

Bases: DataError

+

Raised when data types are incompatible.

+
+ +
+
+exception scitex.logging.PathError(message, context=None, suggestion=None)[source]
+

Bases: SciTeXError

+

Base class for path-related errors.

+
+ +
+
+exception scitex.logging.InvalidPathError(path, reason)[source]
+

Bases: PathError

+

Raised when a path is invalid or doesn’t follow SciTeX conventions.

+
+ +
+
+exception scitex.logging.PathNotFoundError(path)[source]
+

Bases: PathError

+

Raised when a required path doesn’t exist.

+
+ +
+
+exception scitex.logging.TemplateError(message, context=None, suggestion=None)[source]
+

Bases: SciTeXError

+

Base class for template-related errors.

+
+ +
+
+exception scitex.logging.TemplateViolationError(filepath, violation)[source]
+

Bases: TemplateError

+

Raised when SciTeX template is not followed.

+
+ +
+
+exception scitex.logging.NNError(message, context=None, suggestion=None)[source]
+

Bases: SciTeXError

+

Base class for neural network module errors.

+
+ +
+
+exception scitex.logging.ModelError(model_name, reason)[source]
+

Bases: NNError

+

Raised when there are issues with neural network models.

+
+ +
+
+exception scitex.logging.StatsError(message, context=None, suggestion=None)[source]
+

Bases: SciTeXError

+

Base class for statistics module errors.

+
+ +
+
+exception scitex.logging.TestError(test_name, reason)[source]
+

Bases: StatsError

+

Raised when statistical tests fail.

+
+ +
+
+scitex.logging.check_path(path)[source]
+

Validate a path according to SciTeX conventions.

+
+
Return type:
+

None

+
+
+
+ +
+
+scitex.logging.check_file_exists(filepath)[source]
+

Check if a file exists.

+
+
Return type:
+

None

+
+
+
+ +
+
+scitex.logging.check_shape_compatibility(shape1, shape2, operation)[source]
+

Check if two shapes are compatible for an operation.

+
+
Return type:
+

None

+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.nn.html b/src/scitex/_sphinx_html/api/scitex.nn.html new file mode 100644 index 000000000..cbdd40de0 --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.nn.html @@ -0,0 +1,680 @@ + + + + + + + + + scitex.nn API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.nn API Reference

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.path.html b/src/scitex/_sphinx_html/api/scitex.path.html new file mode 100644 index 000000000..4e4d28df2 --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.path.html @@ -0,0 +1,1146 @@ + + + + + + + + + scitex.path API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.path API Reference

+

scitex-path: Scientific project path utilities (find, split, symlink, versioning).

+
+
+scitex.path.clean(path_string)[source]
+

Cleans and normalizes a file system path string.

+
+

Example

+
>>> clean('/home/user/./folder/../file.txt')
+'/home/user/file.txt'
+>>> clean('path/./to//file.txt')
+'path/to/file.txt'
+>>> clean('path with spaces')
+'path_with_spaces'
+
+
+
+
+
Parameters:
+

path_string (str) – File path to clean

+
+
Returns:
+

Normalized path string

+
+
Return type:
+

str

+
+
+
+ +
+ +

Create a relative symbolic link.

+

This is a convenience wrapper around symlink() with relative=True.

+
+
Parameters:
+
    +
  • src (Union[str, Path]) – Source path (target of the symlink)

  • +
  • dst (Union[str, Path]) – Destination path (the symlink to create)

  • +
  • overwrite (bool) – If True, remove existing dst before creating symlink

  • +
+
+
Return type:
+

Path

+
+
Returns:
+

Path object of the created symlink

+
+
+
+ +
+
+scitex.path.find_dir(root_dir, exp)[source]
+

Find directories matching pattern.

+
+
Return type:
+

List[str]

+
+
+
+ +
+
+scitex.path.find_file(root_dir, exp)[source]
+

Find files matching pattern.

+
+
Return type:
+

List[str]

+
+
+
+ +
+
+scitex.path.find_git_root()[source]
+

Find the root directory of the current git repository.

+
+
Returns:
+

Path to the git repository root.

+
+
Return type:
+

str

+
+
+
+ +
+
+scitex.path.find_latest(dirname, fname, ext, version_prefix='_v')[source]
+

Find the latest versioned file in a directory.

+
+
Parameters:
+
    +
  • dirname (str) – Directory to search in.

  • +
  • fname (str) – Base filename without version number or extension.

  • +
  • ext (str) – File extension including the dot (e.g., ‘.txt’).

  • +
  • version_prefix (str, optional) – Prefix before the version number. Default is ‘_v’.

  • +
+
+
Returns:
+

Path to the latest versioned file, or None if not found.

+
+
Return type:
+

str or None

+
+
+
+ +
+ +

Find and optionally fix broken symbolic links.

+
+
Parameters:
+
    +
  • directory (Union[str, Path]) – Directory to search

  • +
  • recursive (bool) – If True, search recursively

  • +
  • remove (bool) – If True, remove broken symlinks

  • +
  • new_target (Union[str, Path, None]) – If provided, repoint broken symlinks to this target

  • +
+
+
Return type:
+

dict

+
+
Returns:
+

Dictionary with ‘found’, ‘fixed’, and ‘removed’ lists of paths

+
+
+
+ +
+
+scitex.path.get_data_path_from_a_package(package_str, resource)[source]
+

Get the path to a data file within a package.

+
+
Parameters:
+
    +
  • package_str (str) – The name of the package as a string.

  • +
  • resource (str) – The name of the resource file within the package’s data directory.

  • +
+
+
Returns:
+

The full path to the resource file.

+
+
Return type:
+

Path

+
+
Raises:
+
    +
  • ImportError – If the specified package cannot be found.

  • +
  • FileNotFoundError – If the resource file does not exist in the package’s data directory.

  • +
+
+
+
+ +
+
+scitex.path.get_spath(sfname, makedirs=False)
+

Create a save path based on the calling script’s location.

+
+
Parameters:
+
    +
  • sfname (Union[str, Path]) – The name of the file to be saved.

  • +
  • makedirs (bool) – If True, create the directory structure for the save path.

  • +
+
+
Returns:
+

The full save path for the file.

+
+
Return type:
+

str

+
+
+
+

Example

+
>>> spath = mk_spath('output.txt', makedirs=True)
+
+
+
+
+ +
+
+scitex.path.get_this_path(ipython_fake_path='/tmp/fake.py')
+

Get the path of the calling script.

+
+

Note

+

This function historically captures the caller’s filename via +inspect.stack()[1] but then returns this module’s __file__. +The tests codify that legacy behavior; do not change without +updating callers.

+
+
+
Return type:
+

str

+
+
+
+ +
+
+scitex.path.getsize(path)[source]
+

Get file size in bytes.

+
+
Parameters:
+

path (Union[str, Path]) – Path to file.

+
+
Returns:
+

File size in bytes, or math.nan if file doesn’t exist.

+
+
Return type:
+

Union[int, float]

+
+
Raises:
+

PermissionError – If the file cannot be accessed due to permissions.

+
+
+
+ +
+
+scitex.path.increment_version(dirname, fname, ext, version_prefix='_v')[source]
+

Generate the next version of a filename based on existing versioned files.

+
+
Parameters:
+
    +
  • dirname (str) – Directory to search in.

  • +
  • fname (str) – Base filename without version number or extension.

  • +
  • ext (str) – File extension including the dot (e.g., ‘.txt’).

  • +
  • version_prefix (str, optional) – Prefix before the version number. Default is ‘_v’.

  • +
+
+
Returns:
+

Full path for the next version of the file.

+
+
Return type:
+

str

+
+
+
+

Example

+
>>> increment_version('/path/to/dir', 'myfile', '.txt')
+'/path/to/dir/myfile_v001.txt'
+
+
+
+
+ +
+ +

Check if a path is a symbolic link.

+
+
Parameters:
+

path (Union[str, Path]) – Path to check

+
+
Return type:
+

bool

+
+
Returns:
+

True if path is a symlink, False otherwise

+
+
+
+ +
+ +

List all symbolic links in a directory.

+
+
Parameters:
+
    +
  • directory (Union[str, Path]) – Directory to search

  • +
  • recursive (bool) – If True, search recursively

  • +
+
+
Return type:
+

list[Path]

+
+
Returns:
+

List of Path objects for all symlinks found

+
+
+
+ +
+
+scitex.path.mk_spath(sfname, makedirs=False)[source]
+

Create a save path based on the calling script’s location.

+
+
Parameters:
+
    +
  • sfname (Union[str, Path]) – The name of the file to be saved.

  • +
  • makedirs (bool) – If True, create the directory structure for the save path.

  • +
+
+
Returns:
+

The full save path for the file.

+
+
Return type:
+

str

+
+
+
+

Example

+
>>> spath = mk_spath('output.txt', makedirs=True)
+
+
+
+
+ +
+ +

Return the path to which the symbolic link points.

+
+
Parameters:
+

path (Union[str, Path]) – Symlink path to read

+
+
Return type:
+

Path

+
+
Returns:
+

Path object pointing to the symlink target

+
+
Raises:
+

OSError – If path is not a symlink

+
+
+
+ +
+ +

Resolve all symbolic links in a path.

+
+
Parameters:
+

path (Union[str, Path]) – Path potentially containing symlinks

+
+
Return type:
+

Path

+
+
Returns:
+

Fully resolved absolute path

+
+
+
+ +
+
+scitex.path.split(fpath)[source]
+

Split a file path into directory, filename, and extension.

+
+
Parameters:
+

fpath (Union[str, Path]) – File path to split.

+
+
Returns:
+

(directory with trailing slash, filename without extension, extension)

+
+
Return type:
+

Tuple[str, str, str]

+
+
+
+

Example

+
>>> dirname, fname, ext = split('/path/to/file.txt')
+>>> dirname
+'/path/to/'
+>>> fname
+'file'
+>>> ext
+'.txt'
+
+
+
+
+ +
+ +

Create a symbolic link pointing to src named dst.

+
+
Parameters:
+
    +
  • src (Union[str, Path]) – Source path (target of the symlink)

  • +
  • dst (Union[str, Path]) – Destination path (the symlink to create)

  • +
  • overwrite (bool) – If True, remove existing dst before creating symlink

  • +
  • target_is_directory (Optional[bool]) – On Windows, specify if target is directory (auto-detected if None)

  • +
  • relative (bool) – If True, create relative symlink instead of absolute

  • +
+
+
Return type:
+

Path

+
+
Returns:
+

Path object of the created symlink

+
+
Raises:
+
+
+
+
+

Examples

+
>>> from scitex_path import symlink
+>>> # Create absolute symlink
+>>> symlink("/path/to/source", "/path/to/link")
+
+
+
>>> # Create relative symlink
+>>> symlink("../source", "link", relative=True)
+
+
+
>>> # Overwrite existing symlink
+>>> symlink("/path/to/new_source", "/path/to/link", overwrite=True)
+
+
+
+
+ +
+
+scitex.path.this_path(ipython_fake_path='/tmp/fake.py')[source]
+

Get the path of the calling script.

+
+

Note

+

This function historically captures the caller’s filename via +inspect.stack()[1] but then returns this module’s __file__. +The tests codify that legacy behavior; do not change without +updating callers.

+
+
+
Return type:
+

str

+
+
+
+ +
+ +

Remove a symbolic link.

+
+
Parameters:
+
    +
  • path (Union[str, Path]) – Symlink to remove

  • +
  • missing_ok (bool) – If True, don’t raise error if symlink doesn’t exist

  • +
+
+
Raises:
+
+
+
Return type:
+

None

+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.pd.html b/src/scitex/_sphinx_html/api/scitex.pd.html new file mode 100644 index 000000000..667923e7f --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.pd.html @@ -0,0 +1,680 @@ + + + + + + + + + scitex.pd API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.pd API Reference

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.plt.html b/src/scitex/_sphinx_html/api/scitex.plt.html new file mode 100644 index 000000000..0b9e78e5b --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.plt.html @@ -0,0 +1,686 @@ + + + + + + + + + scitex.plt API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.plt API Reference

+
+

Note

+

scitex.plt wraps matplotlib with data-tracking axes. The plotting API +is migrating to figrecipe. +See PLT Module (stx.plt) for usage documentation.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.repro.html b/src/scitex/_sphinx_html/api/scitex.repro.html new file mode 100644 index 000000000..c523f5acc --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.repro.html @@ -0,0 +1,1139 @@ + + + + + + + + + scitex.repro API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.repro API Reference

+

scitex-repro — Reproducibility utilities for scientific computing.

+

Provides tools for reproducible scientific computing: +- Random state management (RandomStateManager) +- ID generation (gen_ID) +- Timestamp generation (gen_timestamp) +- Array hashing (hash_array)

+
+
+scitex.repro.gen_ID(time_format='%YY-%mM-%dD-%Hh%Mm%Ss', N=8, *, now_fn=None)
+

Generate a unique identifier with timestamp and random characters.

+

Creates a unique ID by combining a formatted timestamp with random +alphanumeric characters. Useful for creating unique experiment IDs, +run identifiers, or temporary file names.

+
+
Parameters:
+
    +
  • time_format (str, optional) – Format string for timestamp portion. Default is “%YY-%mM-%dD-%Hh%Mm%Ss” +which produces “2025Y-05M-31D-12h30m45s” format.

  • +
  • N (int, optional) – Number of random characters to append. Default is 8.

  • +
  • now_fn (callable, optional) – Zero-argument callable returning a datetime-like object with +.strftime(). Defaults to datetime.now. Injection point for +deterministic tests — pass a fake that returns a fixed datetime +instead of mocking datetime globally.

  • +
+
+
Returns:
+

Unique identifier in format “{timestamp}_{random_chars}”

+
+
Return type:
+

str

+
+
+
+

Examples

+
>>> id1 = gen_id()
+>>> print(id1)
+'2025Y-05M-31D-12h30m45s_a3Bc9xY2'
+
+
+
>>> id2 = gen_id(time_format="%Y%m%d", N=4)
+>>> print(id2)
+'20250531_xY9a'
+
+
+
>>> # For experiment tracking
+>>> exp_id = gen_id()
+>>> save_path = f"results/experiment_{exp_id}.pkl"
+
+
+
+
+ +
+
+scitex.repro.gen_id(time_format='%YY-%mM-%dD-%Hh%Mm%Ss', N=8, *, now_fn=None)[source]
+

Generate a unique identifier with timestamp and random characters.

+

Creates a unique ID by combining a formatted timestamp with random +alphanumeric characters. Useful for creating unique experiment IDs, +run identifiers, or temporary file names.

+
+
Parameters:
+
    +
  • time_format (str, optional) – Format string for timestamp portion. Default is “%YY-%mM-%dD-%Hh%Mm%Ss” +which produces “2025Y-05M-31D-12h30m45s” format.

  • +
  • N (int, optional) – Number of random characters to append. Default is 8.

  • +
  • now_fn (callable, optional) – Zero-argument callable returning a datetime-like object with +.strftime(). Defaults to datetime.now. Injection point for +deterministic tests — pass a fake that returns a fixed datetime +instead of mocking datetime globally.

  • +
+
+
Returns:
+

Unique identifier in format “{timestamp}_{random_chars}”

+
+
Return type:
+

str

+
+
+
+

Examples

+
>>> id1 = gen_id()
+>>> print(id1)
+'2025Y-05M-31D-12h30m45s_a3Bc9xY2'
+
+
+
>>> id2 = gen_id(time_format="%Y%m%d", N=4)
+>>> print(id2)
+'20250531_xY9a'
+
+
+
>>> # For experiment tracking
+>>> exp_id = gen_id()
+>>> save_path = f"results/experiment_{exp_id}.pkl"
+
+
+
+
+ +
+
+scitex.repro.gen_timestamp(*, now_fn=None)[source]
+

Generate a timestamp string for file naming.

+

Returns a timestamp in the format YYYY-MMDD-HHMM, suitable for +creating unique filenames or version identifiers.

+
+
Parameters:
+

now_fn (callable, optional) – Zero-argument callable returning a datetime-like object with +.strftime(). Defaults to datetime.now. Injection point for +deterministic tests — pass a fake that returns a fixed datetime +instead of mocking datetime globally.

+
+
Returns:
+

Timestamp string in format “YYYY-MMDD-HHMM”

+
+
Return type:
+

str

+
+
+
+

Examples

+
>>> timestamp = gen_timestamp()
+>>> print(timestamp)
+'2025-0531-1230'
+
+
+
>>> filename = f"experiment_{gen_timestamp()}.csv"
+>>> print(filename)
+'experiment_2025-0531-1230.csv'
+
+
+
+
+ +
+
+scitex.repro.timestamp(*, now_fn=None)
+

Generate a timestamp string for file naming.

+

Returns a timestamp in the format YYYY-MMDD-HHMM, suitable for +creating unique filenames or version identifiers.

+
+
Parameters:
+

now_fn (callable, optional) – Zero-argument callable returning a datetime-like object with +.strftime(). Defaults to datetime.now. Injection point for +deterministic tests — pass a fake that returns a fixed datetime +instead of mocking datetime globally.

+
+
Returns:
+

Timestamp string in format “YYYY-MMDD-HHMM”

+
+
Return type:
+

str

+
+
+
+

Examples

+
>>> timestamp = gen_timestamp()
+>>> print(timestamp)
+'2025-0531-1230'
+
+
+
>>> filename = f"experiment_{gen_timestamp()}.csv"
+>>> print(filename)
+'experiment_2025-0531-1230.csv'
+
+
+
+
+ +
+
+scitex.repro.hash_array(array_data)[source]
+

Generate hash for array data.

+

Creates a deterministic hash for numpy arrays, useful for +verifying data integrity and reproducibility.

+
+
Parameters:
+

array_data (ndarray) – Array to hash

+
+
Returns:
+

16-character hash string

+
+
Return type:
+

str

+
+
+
+

Examples

+
>>> import numpy as np
+>>> data = np.array([1, 2, 3, 4, 5])
+>>> hash1 = hash_array(data)
+>>> hash2 = hash_array(data)
+>>> hash1 == hash2
+True
+
+
+
+
+ +
+
+class scitex.repro.RandomStateManager(seed=42, verbose=False)[source]
+

Bases: object

+

Simple, robust random state manager for scientific computing.

+
+

Examples

+
>>> from scitex_repro import RandomStateManager
+>>>
+>>> # Method 1: Direct usage
+>>> rng = RandomStateManager(seed=42)
+>>> data = rng("data").random(100)
+>>>
+>>> # Verify reproducibility
+>>> rng.verify(data, "my_data")
+
+
+
+
+
+__init__(seed=42, verbose=False)[source]
+

Initialize with automatic module detection.

+
+ +
+
+get_np_generator(name)[source]
+

Get or create a named NumPy random generator.

+
+
Parameters:
+

name (str) – Generator name (e.g., “data”, “model”, “augment”)

+
+
Returns:
+

Independent NumPy random generator

+
+
Return type:
+

numpy.random.Generator

+
+
+
+

Examples

+
>>> rng = RandomStateManager(42)
+>>> gen = rng.get_np_generator("data")
+>>> values = gen.random(100)
+>>> perm = gen.permutation(100)
+
+
+
+
+ +
+
+__call__(name, verbose=None)[source]
+

Get or create a named NumPy random generator.

+

This is a backward compatibility wrapper for get_np_generator(). +Consider using get_np_generator() directly for clarity.

+
+
Parameters:
+
    +
  • name (str) – Generator name

  • +
  • verbose (bool) – Whether to show deprecation warning

  • +
+
+
Returns:
+

NumPy random generator with deterministic seed

+
+
Return type:
+

numpy.random.Generator

+
+
+
+ +
+
+verify(obj, name=None, verbose=True)[source]
+

Verify object matches cached hash (detects broken reproducibility).

+

First call: caches the object’s hash +Later calls: verifies object matches cached hash

+
+
Parameters:
+
    +
  • obj (Any) – Object to verify (array, tensor, data, model weights, etc.) +Supports: numpy arrays, torch tensors, tf tensors, jax arrays, +lists, dicts, pandas dataframes, and basic types

  • +
  • name (str) – Cache name. Auto-generated if not provided.

  • +
+
+
Returns:
+

True if matches cache (or first call), False if different

+
+
Return type:
+

bool

+
+
+
+

Examples

+
>>> data = generate_data()
+>>> rng.verify(data, "train_data")  # First run: caches
+>>> # Next run:
+>>> rng.verify(data, "train_data")  # Verifies match
+
+
+
+
+ +
+
+checkpoint(name='checkpoint')[source]
+

Save current state of all generators.

+
+ +
+
+restore(checkpoint)[source]
+

Restore from checkpoint.

+
+ +
+
+temporary_seed(seed)[source]
+

Context manager for temporary seed change.

+
+ +
+
+get_sklearn_random_state(name)[source]
+

Get a random state for scikit-learn.

+

Scikit-learn uses integers for random_state parameter.

+
+
Parameters:
+

name (str) – Generator name

+
+
Returns:
+

Random state integer for sklearn

+
+
Return type:
+

int

+
+
+
+

Examples

+
>>> rng = RandomStateManager(42)
+>>> from sklearn.model_selection import train_test_split
+>>> X_train, X_test = train_test_split(
+...     X, test_size=0.2,
+...     random_state=rng.get_sklearn_random_state("split")
+... )
+
+
+
+
+ +
+
+get_torch_generator(name)[source]
+

Get or create a named PyTorch generator.

+
+
Parameters:
+

name (str) – Generator name

+
+
Returns:
+

PyTorch generator with deterministic seed

+
+
Return type:
+

torch.Generator

+
+
+
+

Examples

+
>>> rng = RandomStateManager(42)
+>>> gen = rng.get_torch_generator("model")
+>>> torch.randn(5, 5, generator=gen)
+
+
+
+
+ +
+
+get_generator(name)[source]
+

Alias for get_np_generator for compatibility.

+
+ +
+
+clear_cache(patterns=None)[source]
+

Clear verification cache files.

+
+
Parameters:
+

patterns (str | list[str]) – Specific cache patterns to clear. If None, clears all.

+
+
Returns:
+

Number of cache files removed

+
+
Return type:
+

int

+
+
+
+ +
+ +
+
+scitex.repro.get(verbose=False)[source]
+

Get or create the global RandomStateManager instance.

+
+
Parameters:
+

verbose (bool) – Whether to print status messages (default: False)

+
+
Returns:
+

Global instance

+
+
Return type:
+

RandomStateManager

+
+
+
+

Examples

+
>>> from scitex_repro import get
+>>> rng = get()
+>>> data = rng("data").random(100)
+
+
+
+
+ +
+
+scitex.repro.reset(seed=42, verbose=False)[source]
+

Reset global RandomStateManager with new seed.

+
+
Parameters:
+
    +
  • seed (int) – New seed value

  • +
  • verbose (bool) – Whether to print status messages (default: False)

  • +
+
+
Returns:
+

New global instance

+
+
Return type:
+

RandomStateManager

+
+
+
+

Examples

+
>>> from scitex_repro import reset
+>>> rng = reset(seed=123)
+
+
+
+
+ +
+
+scitex.repro.fix_seeds(seed=42, os=True, random=True, np=True, torch=True, tf=False, jax=False, verbose=False, **kwargs)[source]
+

Deprecated: Use RandomStateManager instead.

+

This function maintains backward compatibility with the old fix_seeds API.

+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.scholar.html b/src/scitex/_sphinx_html/api/scitex.scholar.html new file mode 100644 index 000000000..539a76528 --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.scholar.html @@ -0,0 +1,680 @@ + + + + + + + + + scitex.scholar API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.scholar API Reference

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.session.html b/src/scitex/_sphinx_html/api/scitex.session.html new file mode 100644 index 000000000..9e233e5d4 --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.session.html @@ -0,0 +1,680 @@ + + + + + + + + + scitex.session API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.session API Reference

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.social.html b/src/scitex/_sphinx_html/api/scitex.social.html new file mode 100644 index 000000000..1c95e71f3 --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.social.html @@ -0,0 +1,678 @@ + + + + + + + + + scitex.social API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.social API Reference

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.stats.html b/src/scitex/_sphinx_html/api/scitex.stats.html new file mode 100644 index 000000000..47e63176e --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.stats.html @@ -0,0 +1,680 @@ + + + + + + + + + scitex.stats API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.stats API Reference

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.str.html b/src/scitex/_sphinx_html/api/scitex.str.html new file mode 100644 index 000000000..9b2be888d --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.str.html @@ -0,0 +1,680 @@ + + + + + + + + + scitex.str API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.str API Reference

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.template.html b/src/scitex/_sphinx_html/api/scitex.template.html new file mode 100644 index 000000000..1df97908a --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.template.html @@ -0,0 +1,680 @@ + + + + + + + + + scitex.template API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.template API Reference

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/api/scitex.writer.html b/src/scitex/_sphinx_html/api/scitex.writer.html new file mode 100644 index 000000000..bbb19cda9 --- /dev/null +++ b/src/scitex/_sphinx_html/api/scitex.writer.html @@ -0,0 +1,1136 @@ + + + + + + + + + scitex.writer — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.writer

+

SciTeX Writer - LaTeX manuscript compilation system with MCP server.

+
+
Three Interfaces:
    +
  • Python API: import scitex_writer as sw

  • +
  • CLI: scitex-writer <command>

  • +
  • MCP: 30 tools for AI agents

  • +
+
+
Modules:
    +
  • compile: Compile manuscripts to PDF

  • +
  • export: Export manuscript for arXiv submission

  • +
  • project: Clone, info, get_pdf

  • +
  • tables: List, add, remove, csv_to_latex

  • +
  • figures: List, add, remove, convert

  • +
  • bib: List, add, remove, merge

  • +
  • guidelines: IMRAD writing tips

  • +
  • prompts: AI2 Asta integration

  • +
+
+
+
+
+scitex.writer.usage()
+

Get the usage guide with branding applied.

+
+
Returns:
+

Formatted usage guide string.

+
+
Return type:
+

str

+
+
+
+ +
+
+class scitex.writer.Writer(project_dir, name=None, git_strategy='child', branch=None, tag=None)[source]
+

Bases: object

+

LaTeX manuscript compiler.

+
+
+__init__(project_dir, name=None, git_strategy='child', branch=None, tag=None)[source]
+

Initialize for project directory.

+

If directory doesn’t exist, creates new project.

+
+
Parameters:
+
    +
  • project_dir (Path) – Path to project directory.

  • +
  • name (Optional[str]) – Project name (used if creating new project).

  • +
  • git_strategy (Optional[str]) – Git initialization strategy: +- ‘child’: Create isolated git in project directory (default) +- ‘parent’: Use parent git repository +- ‘origin’: Preserve template’s original git history +- None or ‘none’: Disable git initialization

  • +
  • branch (Optional[str]) – Specific branch of template repository to clone. +If None, clones the default branch. Mutually exclusive with tag.

  • +
  • tag (Optional[str]) – Specific tag/release of template repository to clone. +If None, clones the default branch. Mutually exclusive with branch.

  • +
+
+
+
+ +
+
+compile_manuscript(timeout=300, log_callback=None, progress_callback=None)[source]
+

Compile manuscript to PDF with optional live callbacks.

+

Runs scripts/shell/compile_manuscript.sh with configured settings.

+
+
Parameters:
+
    +
  • timeout (int) – Maximum compilation time in seconds (default: 300).

  • +
  • log_callback (Optional[Callable[[str], None]]) – Called with each log line: log_callback(“Running pdflatex…”).

  • +
  • progress_callback (Optional[Callable[[int, str], None]]) – Called with progress: progress_callback(50, “Pass 2/3”).

  • +
+
+
Returns:
+

With success status, PDF path, and errors/warnings.

+
+
Return type:
+

CompilationResult

+
+
+
+

Examples

+
>>> writer = Writer(Path("my_paper"))
+>>> result = writer.compile_manuscript()
+>>> if result.success:
+...     print(f"PDF created: {result.output_pdf}")
+
+
+
+
+ +
+
+compile_supplementary(timeout=300, log_callback=None, progress_callback=None)[source]
+

Compile supplementary materials to PDF with optional live callbacks.

+

Runs scripts/shell/compile_supplementary.sh with configured settings.

+
+
Parameters:
+
+
+
Returns:
+

With success status, PDF path, and errors/warnings.

+
+
Return type:
+

CompilationResult

+
+
+
+

Examples

+
>>> writer = Writer(Path("my_paper"))
+>>> result = writer.compile_supplementary()
+>>> if result.success:
+...     print(f"PDF created: {result.output_pdf}")
+
+
+
+
+ +
+
+compile_revision(track_changes=False, timeout=300, log_callback=None, progress_callback=None)[source]
+

Compile revision document with optional change tracking and live callbacks.

+

Runs scripts/shell/compile_revision.sh with configured settings.

+
+
Parameters:
+
    +
  • track_changes (bool) – Enable change tracking in compiled PDF (default: False).

  • +
  • timeout (int) – Maximum compilation time in seconds (default: 300).

  • +
  • log_callback (Optional[Callable[[str], None]]) – Called with each log line.

  • +
  • progress_callback (Optional[Callable[[int, str], None]]) – Called with progress updates.

  • +
+
+
Returns:
+

With success status, PDF path, and errors/warnings.

+
+
Return type:
+

CompilationResult

+
+
+
+

Examples

+
>>> writer = Writer(Path("my_paper"))
+>>> result = writer.compile_revision(track_changes=True)
+>>> if result.success:
+...     print(f"Revision PDF: {result.output_pdf}")
+
+
+
+
+ +
+
+watch(on_compile=None)[source]
+

Auto-recompile on file changes.

+
+
Return type:
+

None

+
+
+
+ +
+
+get_pdf(doc_type='manuscript')[source]
+

Get output PDF path (Read).

+
+
Return type:
+

Optional[Path]

+
+
+
+ +
+
+delete()[source]
+

Delete project directory (Delete).

+
+
Return type:
+

bool

+
+
+
+ +
+ +
+
+class scitex.writer.CompilationResult(success, exit_code, stdout, stderr, output_pdf=None, diff_pdf=None, log_file=None, duration=0.0, errors=<factory>, warnings=<factory>)[source]
+

Bases: object

+

Result of LaTeX compilation.

+
+
+success: bool
+

Whether compilation succeeded (exit code 0)

+
+ +
+
+exit_code: int
+

Process exit code

+
+ +
+
+stdout: str
+

Standard output from compilation

+
+ +
+
+stderr: str
+

Standard error from compilation

+
+ +
+
+output_pdf: Path | None = None
+

Path to generated PDF (if successful)

+
+ +
+
+diff_pdf: Path | None = None
+

Path to diff PDF with tracked changes (if generated)

+
+ +
+
+log_file: Path | None = None
+

Path to compilation log file

+
+ +
+
+duration: float = 0.0
+

Compilation duration in seconds

+
+ +
+
+errors: List[str]
+

Parsed LaTeX errors (if any)

+
+ +
+
+warnings: List[str]
+

Parsed LaTeX warnings (if any)

+
+ +
+
+__str__()[source]
+

Human-readable summary.

+
+ +
+ +
+
+class scitex.writer.ManuscriptTree(root, git_root=None, contents=None, base=None, readme=None, archive=None)[source]
+

Bases: object

+

Manuscript directory structure (01_manuscript/).

+
+
+root: Path
+
+ +
+
+git_root: Path | None = None
+
+ +
+
+contents: ManuscriptContents = None
+
+ +
+
+base: DocumentSection = None
+
+ +
+
+readme: DocumentSection = None
+
+ +
+
+archive: Path = None
+
+ +
+
+__post_init__()[source]
+

Initialize all instances.

+
+ +
+
+verify_structure()[source]
+

Verify manuscript structure has required components.

+
+
Return type:
+

tuple[bool, list[str]]

+
+
Returns:
+

(is_valid, list_of_missing_items_with_paths)

+
+
+
+ +
+ +
+
+class scitex.writer.SupplementaryTree(root, git_root=None, contents=None, base=None, supplementary=None, supplementary_diff=None, readme=None, archive=None)[source]
+

Bases: object

+

Supplementary directory structure (02_supplementary/).

+
+
+root: Path
+
+ +
+
+git_root: Path | None = None
+
+ +
+
+contents: SupplementaryContents = None
+
+ +
+
+base: DocumentSection = None
+
+ +
+
+supplementary: DocumentSection = None
+
+ +
+
+supplementary_diff: DocumentSection = None
+
+ +
+
+readme: DocumentSection = None
+
+ +
+
+archive: Path = None
+
+ +
+
+__post_init__()[source]
+

Initialize all instances.

+
+ +
+
+verify_structure()[source]
+

Verify supplementary structure has required components.

+
+
Return type:
+

tuple[bool, list[str]]

+
+
Returns:
+

(is_valid, list_of_missing_items_with_paths)

+
+
+
+ +
+ +
+
+class scitex.writer.RevisionTree(root, git_root=None, contents=None, base=None, revision=None, readme=None, archive=None, docs=None)[source]
+

Bases: object

+

Revision directory structure (03_revision/).

+
+
+root: Path
+
+ +
+
+git_root: Path | None = None
+
+ +
+
+contents: RevisionContents = None
+
+ +
+
+base: DocumentSection = None
+
+ +
+
+revision: DocumentSection = None
+
+ +
+
+readme: DocumentSection = None
+
+ +
+
+archive: Path = None
+
+ +
+
+docs: Path = None
+
+ +
+
+__post_init__()[source]
+

Initialize all instances.

+
+ +
+
+verify_structure()[source]
+

Verify revision structure has required components.

+
+
Return type:
+

tuple[bool, list[str]]

+
+
Returns:
+

(is_valid, list_of_missing_items_with_paths)

+
+
+
+ +
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/core_concepts.html b/src/scitex/_sphinx_html/core_concepts.html new file mode 100644 index 000000000..313079462 --- /dev/null +++ b/src/scitex/_sphinx_html/core_concepts.html @@ -0,0 +1,856 @@ + + + + + + + + + Core Concepts — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Core Concepts

+
+

Research Workflow

+

SciTeX covers the full research pipeline, from literature review to publication:

+Research workflow: Question → Literature → Data → Analysis → Figures → Manuscript → Verify → Publication + +

Each stage maps to a SciTeX module. The @stx.session decorator ties them +together, ensuring every step is reproducible and provenance-tracked.

+
+
+

Architecture

+

SciTeX is organized around a simple principle: every research script should be +a reproducible, self-documenting unit of work. The @stx.session decorator +enforces this by managing outputs, logging, and configuration automatically.

+Module architecture: Experiment (session, config, io, logging, repro), Analysis & Visualization (stats, dsp, plt, diagram), Publication (scholar, writer, clew) + +

Modules are grouped into three layers – Experiment infrastructure (blue), +Analysis & Visualization tools (orange), and Publication (purple). +See Module Overview for the full module reference.

+
+
+

The Session Model

+

@stx.session is the core abstraction. It wraps a function and provides:

+
    +
  1. Output directory: script_out/FINISHED_SUCCESS/<session_id>/

  2. +
  3. Logging: All stdout/stderr captured to script.log

  4. +
  5. Config injection: YAML files from ./config/ merged and injected

  6. +
  7. CLI generation: Function parameters become --flags

  8. +
  9. Provenance: File hashes recorded to SQLite for reproducibility

  10. +
+
import scitex as stx
+
+@stx.session
+def main(
+    lr=0.001,               # --lr 0.01
+    epochs=100,             # --epochs 50
+    CONFIG=stx.INJECTED,    # from ./config/*.yaml
+    plt=stx.INJECTED,       # pre-configured matplotlib
+    logger=stx.INJECTED,    # session logger
+):
+    """Train a model. Docstring becomes --help text."""
+    logger.info(f"Training with lr={lr}, epochs={epochs}")
+
+    # stx.io.save paths are relative to session output dir
+    stx.io.save({"lr": lr, "epochs": epochs}, "params.yaml")
+
+    return 0
+
+
+

Session output tree:

+
train_out/
+├── RUNNING/                    # while script runs
+│   └── 20260213_143022_AB12/
+│       ├── params.yaml
+│       └── train.log
+└── FINISHED_SUCCESS/           # after successful exit
+    └── 20260213_143022_AB12/   # moved here on completion
+        ├── params.yaml
+        └── train.log
+
+
+
+
+

Universal I/O

+

stx.io.save and stx.io.load dispatch on file extension:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Extension

Data Type

Backend

.csv

DataFrame

pandas

.npy, .npz

ndarray

numpy

.pkl, .pickle

any object

pickle

.yaml, .yml

dict

PyYAML

.json

dict/list

json

.png, .jpg, .svg, .pdf

Figure

matplotlib

.hdf5, .h5

dict/array

h5py

.mat

dict

scipy.io

.pth, .pt

state_dict

torch

.parquet

DataFrame

pyarrow

+

When saving a matplotlib Figure, SciTeX also exports:

+
    +
  • A .csv with the plotted data (extracted from axes)

  • +
  • A .yaml recipe for reproducing the figure

  • +
+
+
+

Provenance Tracking (Clew)

+

Inside @stx.session, every stx.io.save and stx.io.load call +records the file’s SHA-256 hash to a local SQLite database. This enables:

+
    +
  • Verification: Check if output files have been modified since creation

  • +
  • DAG reconstruction: Trace which inputs produced which outputs

  • +
  • Cross-session linking: If script B loads a file that script A produced, +the parent-child relationship is recorded automatically

  • +
+
# Check verification status
+scitex clew status
+
+# Verify a specific session
+scitex clew run <session_id>
+
+# Generate a dependency diagram
+scitex clew mermaid
+
+
+
+
+

Configuration

+

SciTeX uses a priority-based config system:

+
    +
  1. CLI flags (highest priority): --lr 0.01

  2. +
  3. Config files: ./config/*.yaml (merged alphabetically)

  4. +
  5. Function defaults (lowest priority): lr=0.001

  6. +
+
# config/experiment.yaml
+DATA_DIR: ./data
+MODEL:
+  hidden_size: 256
+  dropout: 0.1
+
+# config/PATH.yaml
+OUTPUT_DIR: ${HOME}/results    # env var substitution
+
+
+

Access in code:

+
@stx.session
+def main(CONFIG=stx.INJECTED, **kw):
+    data_dir = CONFIG["DATA_DIR"]
+    hidden = CONFIG["MODEL"]["hidden_size"]
+
+
+
+
+

Best Practices

+
    +
  1. One session per script: Each .py file should have one @stx.session function

  2. +
  3. Use relative paths in save/load: They resolve relative to the session output directory

  4. +
  5. Return 0 for success: The exit code determines the output directory name (FINISHED_SUCCESS vs FINISHED_ERROR)

  6. +
  7. Put config in ./config/*.yaml: Keeps parameters separate from code

  8. +
  9. Use stx.repro.fix_seeds(42) for determinism: Fixes numpy, torch, random seeds in one call

  10. +
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/gallery.html b/src/scitex/_sphinx_html/gallery.html new file mode 100644 index 000000000..fd2bc8431 --- /dev/null +++ b/src/scitex/_sphinx_html/gallery.html @@ -0,0 +1,1023 @@ + + + + + + + + + Plot Gallery — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ + + + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/genindex.html b/src/scitex/_sphinx_html/genindex.html new file mode 100644 index 000000000..d8eb9ecc4 --- /dev/null +++ b/src/scitex/_sphinx_html/genindex.html @@ -0,0 +1,2550 @@ + + + + + + + + Index — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ + +

Index

+ +
+ _ + | A + | B + | C + | D + | E + | F + | G + | H + | I + | J + | L + | M + | N + | O + | P + | Q + | R + | S + | T + | U + | V + | W + | Y + +
+

_

+ + + +
+ +

A

+ + + +
+ +

B

+ + + +
+ +

C

+ + + +
+ +

D

+ + + +
+ +

E

+ + + +
+ +

F

+ + + +
+ +

G

+ + + +
+ +

H

+ + + +
+ +

I

+ + + +
+ +

J

+ + + +
+ +

L

+ + + +
+ +

M

+ + + +
+ +

N

+ + + +
+ +

O

+ + + +
+ +

P

+ + + +
+ +

Q

+ + +
+ +

R

+ + + +
+ +

S

+ + + +
+ +

T

+ + + +
+ +

U

+ + + +
+ +

V

+ + + +
+ +

W

+ + + +
+ +

Y

+ + +
+ + + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/index.html b/src/scitex/_sphinx_html/index.html new file mode 100644 index 000000000..cda5bd9dc --- /dev/null +++ b/src/scitex/_sphinx_html/index.html @@ -0,0 +1,843 @@ + + + + + + + + + SciTeX – Modular Python Toolkit for Researchers and AI Agents — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • + Edit on GitHub +
  • +
+
+
+
+
+ +
+

SciTeX – Modular Python Toolkit for Researchers and AI Agents

+

A Python framework for reproducible scientific research.

+SciTeX Ecosystem + +
+

Role in SciTeX Ecosystem

+

scitex is the unified orchestrator package. It re-exports from sub-packages +so users have a single import (import scitex). It does not contain runtime logic +itself – it delegates to sub-packages.

+
scitex (this package) -- orchestrator, templates, CLI, MCP server
+  |-- scitex.app  <-  scitex-app   (runtime SDK: file I/O, config, validation)
+  |-- scitex.ui   <-  scitex-ui    (React/TS components: workspace, data-table)
+  +-- scitex.plt  <-  figrecipe    (figures: plotting, diagrams, recipes)
+
+
+
    +
  • scitex-app (docs): Runtime SDK that apps import at execution time

  • +
  • scitex-ui (docs): Shared React/TypeScript component library

  • +
  • figrecipe (docs): Reference app – figures, diagrams, recipes

  • +
+
+
+

Four Freedoms for Research

+
    +
  1. The freedom to run your research anywhere – your machine, your terms.

  2. +
  3. The freedom to study how every step works – from raw data to final manuscript.

  4. +
  5. The freedom to redistribute your workflows, not just your papers.

  6. +
  7. The freedom to modify any module and share improvements with the community.

  8. +
+

AGPL-3.0 – because research infrastructure deserves the same freedoms as the software it runs on.

+
import scitex as stx
+
+@stx.session
+def main(n_samples=100, plt=stx.INJECTED):
+    import numpy as np
+    x = np.linspace(0, 2 * np.pi, n_samples)
+    y = np.sin(x) + np.random.normal(0, 0.1, n_samples)
+
+    fig, ax = plt.subplots()
+    ax.plot_line(x, y)
+    stx.io.save(fig, "sine.png")       # PNG + CSV + YAML recipe
+    return 0
+
+
+
    +
  • @stx.session – Reproducible runs with CLI, config, and provenance tracking

  • +
  • stx.io – Save/load 30+ formats through a single interface

  • +
  • stx.plt – Publication figures with auto CSV data export

  • +
  • stx.stats – 23 statistical tests with effect sizes and CI

  • +
  • stx.scholar – Search, download, and enrich papers

  • +
  • 120+ MCP tools for AI-assisted research workflows

  • +
+
+

Getting Started

+ +
+
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/installation.html b/src/scitex/_sphinx_html/installation.html new file mode 100644 index 000000000..73ac810bd --- /dev/null +++ b/src/scitex/_sphinx_html/installation.html @@ -0,0 +1,845 @@ + + + + + + + + + Installation — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Installation

+
+

Requirements

+
    +
  • Python 3.10+

  • +
  • uv (strongly recommended) — install with pip install uv or +curl -LsSf https://astral.sh/uv/install.sh | sh

  • +
  • pip 21+ also works, but expect 30–90 min for scitex[all] +(see warning below)

  • +
+
+

Warning

+

pip install "scitex[all]" typically takes 30–90 minutes because +pip’s serial resolver walks version histories of the large transitive +dependency set (numpy/pandas/torch/jax/playwright/openalex-local/…). +Use uv instead — it resolves the same set in parallel in 1–3 +minutes. Every pip install line on this page also works as +uv pip install and we recommend the uv form.

+
+
+
+

Core Install

+

The base package pulls in only lightweight dependencies and gives you access +to session management, path utilities, string helpers, and the module +discovery system.

+
uv pip install scitex          # recommended
+pip install scitex              # also works, slower for [all]
+
+
+
+ +
+

Research Workflow

+

For a typical research project you need figures, statistics, and literature +search but not audio, browser automation, or cloud tools:

+
pip install "scitex[plt,stats,scholar]"
+
+
+
+
+

Per-Module Extras

+

Install only what you need. Each extra maps to a self-contained capability.

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Extra

Description

plt

Publication-ready figures via figrecipe (matplotlib, seaborn, Pillow)

stats

Hypothesis testing, effect sizes, power analysis (scitex-stats, scipy, statsmodels)

io

Unified I/O for 40+ formats (HDF5, Excel, YAML, PDF, images, …)

scholar

Literature search and PDF management (CrossRef, OpenAlex, Semantic Scholar)

writer

LaTeX manuscript compilation, BibTeX management, Overleaf export

audio

Text-to-speech and audio utilities (scitex-audio)

ai

LLM APIs (OpenAI, Anthropic, Google, Groq) and ML tools (scikit-learn)

browser

Web automation via Playwright

capture

Screenshot capture (mss, Playwright)

dataset

Scientific dataset access (DANDI, OpenNeuro, PhysioNet)

cloud

Cloud integration utilities

app

Unified file storage SDK (scitex-app)

session

Session decorator with reproducibility logging

diagram

Diagram generation (Mermaid, Graphviz)

db

Database access (SQLAlchemy, PostgreSQL)

cv

Computer vision (OpenCV, Pillow)

dsp

Digital signal processing (scipy, tensorpac)

social

Social media posting (socialia)

tunnel

SSH tunnel management (scitex-tunnel)

all

Everything above

+

Example combinations:

+
# Neuroscience analysis
+pip install "scitex[plt,stats,dsp,dataset]"
+
+# Paper writing
+pip install "scitex[writer,scholar,plt]"
+
+# AI agent development
+pip install "scitex[ai,browser,capture]"
+
+
+
+
+

Development Install

+

Clone the repository and install in editable mode with development tools:

+
git clone https://github.com/ywatanabe1989/scitex-python.git
+cd scitex-python
+pip install -e ".[dev]"
+
+
+

The dev extra includes pytest, ruff, mypy, Sphinx, and build tools.

+ +
+
+

Verifying the Installation

+
import scitex as stx
+print(stx.__version__)
+
+
+

To check which optional modules are available:

+
stx.usage.list()
+
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/ai.html b/src/scitex/_sphinx_html/modules/ai.html new file mode 100644 index 000000000..6dec49388 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/ai.html @@ -0,0 +1,760 @@ + + + + + + + + + AI Module (stx.ai) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

AI Module (stx.ai)

+

Machine learning utilities for training, classification, and metrics +with PyTorch and scikit-learn.

+
+

Quick Reference

+
import scitex as stx
+
+# Training utilities
+from scitex.ai import LearningCurveLogger, EarlyStopping
+
+logger = LearningCurveLogger()
+stopper = EarlyStopping(patience=10, direction="minimize")
+
+for epoch in range(100):
+    # ... training loop ...
+    logger({"loss": loss, "acc": acc}, step="Training")
+    if stopper(val_loss, {"model": model_path}, epoch):
+        break
+
+logger.plot_learning_curves(spath="curves.png")
+
+# Classification
+from scitex.ai import ClassificationReporter, Classifier
+
+clf = Classifier()("SVC")
+reporter = ClassificationReporter(output_dir="./results")
+reporter.calculate_metrics(y_true, y_pred, y_proba)
+reporter.save_summary()
+
+
+
+
+

Training

+
    +
  • LearningCurveLogger – Track and visualize training/validation/test metrics across epochs

  • +
  • EarlyStopping – Monitor validation metrics and stop when improvement plateaus

  • +
+
+
+

Classification

+
    +
  • ClassificationReporter – Unified reporter for single/multi-task classification (balanced accuracy, MCC, ROC-AUC, confusion matrices)

  • +
  • Classifier – Factory for scikit-learn classifiers (SVC, KNN, Logistic Regression, AdaBoost, …)

  • +
  • CrossValidationExperiment – Cross-validation framework

  • +
+
+
+

Metrics

+

Standardized calc_* functions:

+
    +
  • calc_bacc – Balanced accuracy

  • +
  • calc_mcc – Matthews Correlation Coefficient

  • +
  • calc_conf_mat – Confusion matrix

  • +
  • calc_roc_auc – ROC-AUC score

  • +
  • calc_pre_rec_auc – Precision-Recall AUC

  • +
  • calc_feature_importance – Feature importance scores

  • +
+
+
+

Visualization

+
    +
  • plot_learning_curve – Training/validation curves

  • +
  • stx_conf_mat – Confusion matrix heatmap

  • +
  • plot_roc_curve – ROC curve

  • +
  • plot_pre_rec_curve – Precision-Recall curve

  • +
  • plot_feature_importance – Feature importance bar plots

  • +
+
+
+

Other

+
    +
  • MultiTaskLoss – Multi-task learning loss weighting

  • +
  • get_optimizer / set_optimizer – Optimizer management

  • +
  • GenAI – Generative AI wrapper (lazy-loaded)

  • +
  • Clustering: pca, umap

  • +
+
+
+

API Reference

+

See scitex.ai API Reference for the auto-generated Python API.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/app.html b/src/scitex/_sphinx_html/modules/app.html new file mode 100644 index 000000000..f5c836daf --- /dev/null +++ b/src/scitex/_sphinx_html/modules/app.html @@ -0,0 +1,681 @@ + + + + + + + + + app Module (stx.app) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

app Module (stx.app)

+

See /api/scitex.app for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/audio.html b/src/scitex/_sphinx_html/modules/audio.html new file mode 100644 index 000000000..551fb4cd3 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/audio.html @@ -0,0 +1,681 @@ + + + + + + + + + audio Module (stx.audio) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

audio Module (stx.audio)

+

See /api/scitex.audio for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/audit.html b/src/scitex/_sphinx_html/modules/audit.html new file mode 100644 index 000000000..e1f07bbe3 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/audit.html @@ -0,0 +1,681 @@ + + + + + + + + + audit Module (stx.audit) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

audit Module (stx.audit)

+

See /api/scitex.audit for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/benchmark.html b/src/scitex/_sphinx_html/modules/benchmark.html new file mode 100644 index 000000000..d79cc2003 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/benchmark.html @@ -0,0 +1,681 @@ + + + + + + + + + benchmark Module (stx.benchmark) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

benchmark Module (stx.benchmark)

+

See /api/scitex.benchmark for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/bridge.html b/src/scitex/_sphinx_html/modules/bridge.html new file mode 100644 index 000000000..78448f88f --- /dev/null +++ b/src/scitex/_sphinx_html/modules/bridge.html @@ -0,0 +1,681 @@ + + + + + + + + + bridge Module (stx.bridge) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

bridge Module (stx.bridge)

+

See /api/scitex.bridge for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/browser.html b/src/scitex/_sphinx_html/modules/browser.html new file mode 100644 index 000000000..4aa483ffd --- /dev/null +++ b/src/scitex/_sphinx_html/modules/browser.html @@ -0,0 +1,681 @@ + + + + + + + + + browser Module (stx.browser) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

browser Module (stx.browser)

+

See /api/scitex.browser for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/canvas.html b/src/scitex/_sphinx_html/modules/canvas.html new file mode 100644 index 000000000..5b2a3a317 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/canvas.html @@ -0,0 +1,681 @@ + + + + + + + + + canvas Module (stx.canvas) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

canvas Module (stx.canvas)

+

See /api/scitex.canvas for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/capture.html b/src/scitex/_sphinx_html/modules/capture.html new file mode 100644 index 000000000..dc3e00fc2 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/capture.html @@ -0,0 +1,681 @@ + + + + + + + + + capture Module (stx.capture) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

capture Module (stx.capture)

+

See /api/scitex.capture for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/clew.html b/src/scitex/_sphinx_html/modules/clew.html new file mode 100644 index 000000000..2445ffcdb --- /dev/null +++ b/src/scitex/_sphinx_html/modules/clew.html @@ -0,0 +1,768 @@ + + + + + + + + + Clew Module (stx.clew) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Clew Module (stx.clew)

+

Hash-based provenance tracking for reproducible science. Clew (Ariadne’s +thread) records file hashes during @stx.session runs and traces +dependency chains back to source.

+
+

How It Works

+
    +
  1. @stx.session starts a tracking session

  2. +
  3. stx.io.load() records input file hashes

  4. +
  5. stx.io.save() records output file hashes

  6. +
  7. Session close computes a combined hash of all inputs/outputs

  8. +
  9. Later, stx.clew can verify nothing has changed

  10. +
+
import scitex as stx
+
+# Automatic -- just use @stx.session + stx.io
+@stx.session
+def main():
+    data = stx.io.load("input.csv")      # Tracked as input
+    result = process(data)
+    stx.io.save(result, "output.png")     # Tracked as output
+    return 0
+
+# Verify later
+stx.clew.status()                         # Like git status
+stx.clew.run("session_id")                # Verify by hash
+stx.clew.chain("output.png")              # Trace to source
+
+
+
+
+

CLI Commands

+
scitex clew status                  # Show changed files
+scitex clew list                    # List all tracked runs
+scitex clew run <session_id>        # Verify a specific run
+scitex clew chain <file>            # Trace dependency chain
+scitex clew stats                   # Database statistics
+
+
+
+
+

Verification Levels

+
    +
  • CACHE – Hash comparison only (fast). Checks if files match stored hashes.

  • +
  • RERUN – Re-execute scripts and compare outputs (thorough). Catches logic errors.

  • +
+
# Fast: hash comparison
+result = stx.clew.run("session_id")
+
+# Thorough: re-execute and compare
+result = stx.clew.run("session_id", from_scratch=True)
+
+
+
+
+

Dependency Chains

+

Clew traces parent_session links to build a DAG from final output +back to original source:

+
chain = stx.clew.chain("final_figure.png")
+# Shows: source.py → intermediate.csv → analysis.py → final_figure.png
+
+# Visualize as Mermaid DAG
+stx.clew.mermaid("session_id")
+
+
+
+
+

Verification Statuses

+
    +
  • VERIFIED – Files match expected hashes

  • +
  • MISMATCH – Files differ from stored hashes

  • +
  • MISSING – Files no longer exist

  • +
  • UNKNOWN – No prior tracking data

  • +
+
+
+

Key Functions

+
    +
  • status() – Show changed items (like git status)

  • +
  • run(session_id) – Verify a specific run

  • +
  • chain(target_file) – Trace dependency chain

  • +
  • list_runs(limit, status) – List tracked runs

  • +
  • stats() – Database statistics

  • +
+
+
+

API Reference

+

See scitex.clew API Reference for the auto-generated Python API.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/cli.html b/src/scitex/_sphinx_html/modules/cli.html new file mode 100644 index 000000000..d10420179 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/cli.html @@ -0,0 +1,681 @@ + + + + + + + + + cli Module (stx.cli) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

cli Module (stx.cli)

+

See /api/scitex.cli for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/cloud.html b/src/scitex/_sphinx_html/modules/cloud.html new file mode 100644 index 000000000..c4bd5d74f --- /dev/null +++ b/src/scitex/_sphinx_html/modules/cloud.html @@ -0,0 +1,681 @@ + + + + + + + + + cloud Module (stx.cloud) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

cloud Module (stx.cloud)

+

See /api/scitex.cloud for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/compat.html b/src/scitex/_sphinx_html/modules/compat.html new file mode 100644 index 000000000..e5b5b5f77 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/compat.html @@ -0,0 +1,681 @@ + + + + + + + + + compat Module (stx.compat) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

compat Module (stx.compat)

+

See /api/scitex.compat for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/config.html b/src/scitex/_sphinx_html/modules/config.html new file mode 100644 index 000000000..cfeb85715 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/config.html @@ -0,0 +1,758 @@ + + + + + + + + + Config Module (stx.config) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Config Module (stx.config)

+

YAML-based configuration with priority resolution and path management.

+
+

Priority Resolution

+

All config values follow the same precedence:

+
direct argument > config/YAML > environment variable > default
+
+
+
+
+

Quick Reference

+
import scitex as stx
+
+# Get global config (reads ~/.scitex/config.yaml)
+config = stx.config.get_config()
+
+# Resolve with precedence
+log_level = config.resolve("logging.level", default="INFO")
+# Checks: direct → YAML → SCITEX_LOGGING_LEVEL env → "INFO"
+
+# Access nested values
+config.get_nested("scholar", "crossref_email")
+
+# Path management
+paths = stx.config.get_paths()
+paths.scholar        # ~/.scitex/scholar
+paths.cache          # ~/.scitex/cache
+
+
+
+
+

YAML Configuration

+
# ~/.scitex/config.yaml (or ./config/*.yaml for projects)
+logging:
+  level: INFO
+
+scholar:
+  crossref_email: ${CROSSREF_EMAIL:-user@example.com}
+
+
+

Environment variable substitution with ${VAR:-default} syntax is +supported in YAML files.

+
+
+

Key Classes

+
    +
  • ScitexConfig – YAML-based config with env var substitution and precedence resolution

  • +
  • PriorityConfig – Dict-based config resolver for programmatic use

  • +
  • ScitexPaths – Centralized path manager (all paths derive from ~/.scitex/)

  • +
  • EnvVar – Environment variable definition dataclass

  • +
+
+
+

Path Management

+

ScitexPaths manages all SciTeX directories:

+
paths = stx.config.get_paths()
+paths.base       # ~/.scitex (SCITEX_DIR)
+paths.logs       # ~/.scitex/logs
+paths.cache      # ~/.scitex/cache
+paths.scholar    # ~/.scitex/scholar
+paths.rng        # ~/.scitex/rng
+paths.capture    # ~/.scitex/capture
+
+
+
+
+

Utility Functions

+
    +
  • get_config(path) – Get singleton ScitexConfig instance

  • +
  • get_paths(base_dir) – Get singleton ScitexPaths instance

  • +
  • load_yaml(path) – Load YAML with env var substitution

  • +
  • load_dotenv(path) – Load .env file

  • +
  • get_scitex_dir() – Get SCITEX_DIR with precedence

  • +
+
+
+

API Reference

+

See scitex.config API Reference for the auto-generated Python API.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/container.html b/src/scitex/_sphinx_html/modules/container.html new file mode 100644 index 000000000..52dda4a58 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/container.html @@ -0,0 +1,681 @@ + + + + + + + + + container Module (stx.container) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

container Module (stx.container)

+

See /api/scitex.container for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/context.html b/src/scitex/_sphinx_html/modules/context.html new file mode 100644 index 000000000..9625abb59 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/context.html @@ -0,0 +1,681 @@ + + + + + + + + + context Module (stx.context) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

context Module (stx.context)

+

See /api/scitex.context for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/cv.html b/src/scitex/_sphinx_html/modules/cv.html new file mode 100644 index 000000000..49e243330 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/cv.html @@ -0,0 +1,681 @@ + + + + + + + + + cv Module (stx.cv) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

cv Module (stx.cv)

+

See /api/scitex.cv for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/dataset.html b/src/scitex/_sphinx_html/modules/dataset.html new file mode 100644 index 000000000..1316e5c59 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/dataset.html @@ -0,0 +1,681 @@ + + + + + + + + + dataset Module (stx.dataset) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

dataset Module (stx.dataset)

+

See /api/scitex.dataset for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/datetime.html b/src/scitex/_sphinx_html/modules/datetime.html new file mode 100644 index 000000000..ae6d1eac9 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/datetime.html @@ -0,0 +1,681 @@ + + + + + + + + + datetime Module (stx.datetime) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

datetime Module (stx.datetime)

+

See /api/scitex.datetime for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/db.html b/src/scitex/_sphinx_html/modules/db.html new file mode 100644 index 000000000..141ae2050 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/db.html @@ -0,0 +1,681 @@ + + + + + + + + + db Module (stx.db) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

db Module (stx.db)

+

See scitex.db API Reference for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/decorators.html b/src/scitex/_sphinx_html/modules/decorators.html new file mode 100644 index 000000000..59cea2278 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/decorators.html @@ -0,0 +1,756 @@ + + + + + + + + + Decorators Module (stx.decorators) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Decorators Module (stx.decorators)

+

Function decorators for type conversion, batching, caching, and more.

+
+

Quick Reference

+
from scitex.decorators import torch_fn, batch_fn, cache_disk, timeout
+
+@torch_fn
+def process(x):
+    """Auto-converts inputs to torch tensors, outputs back."""
+    return x * 2
+
+@batch_fn
+def predict(data, batch_size=32):
+    """Process data in batches with progress bar."""
+    return model(data)
+
+@cache_disk
+def expensive_computation(params):
+    """Results cached to disk for reuse."""
+    return heavy_compute(params)
+
+@timeout(seconds=30)
+def risky_call():
+    """Raises TimeoutError if exceeds 30s."""
+    return external_api()
+
+
+
+
+

Type Conversion

+

Auto-convert between data frameworks:

+
    +
  • @torch_fn – Inputs to PyTorch tensors, outputs back to original type

  • +
  • @numpy_fn – Inputs to NumPy arrays

  • +
  • @pandas_fn – Inputs to pandas objects

  • +
  • @xarray_fn – Inputs to xarray objects

  • +
+
+
+

Batch Processing

+
    +
  • @batch_fn – Split input into batches with tqdm progress

  • +
  • @batch_numpy_fn – NumPy conversion + batching

  • +
  • @batch_torch_fn – PyTorch conversion + batching

  • +
  • @batch_pandas_fn – Pandas conversion + batching

  • +
+
+
+

Caching

+
    +
  • @cache_mem – In-memory function result caching

  • +
  • @cache_disk – Persistent disk-based caching

  • +
  • @cache_disk_async – Async disk caching

  • +
+
+
+

Utilities

+
    +
  • @deprecated(reason, forward_to) – Mark functions as deprecated

  • +
  • @not_implemented – Mark as not yet implemented

  • +
  • @timeout(seconds) – Enforce execution time limits

  • +
  • @preserve_doc – Preserve docstrings when wrapping

  • +
+
+
+

Auto-Ordering

+
from scitex.decorators import enable_auto_order
+
+enable_auto_order()
+# Now decorators are applied in optimal order regardless of code order
+
+
+
+
+

API Reference

+

See scitex.decorators API Reference for the auto-generated Python API.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/dev.html b/src/scitex/_sphinx_html/modules/dev.html new file mode 100644 index 000000000..c60783aa5 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/dev.html @@ -0,0 +1,681 @@ + + + + + + + + + dev Module (stx.dev) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

dev Module (stx.dev)

+

See /api/scitex.dev for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/diagram.html b/src/scitex/_sphinx_html/modules/diagram.html new file mode 100644 index 000000000..dde45cf27 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/diagram.html @@ -0,0 +1,775 @@ + + + + + + + + + Diagram Module (stx.diagram) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Diagram Module (stx.diagram)

+

Paper-optimized diagram generation with Mermaid and Graphviz backends.

+
+

Quick Reference

+
from scitex.diagram import Diagram
+
+# Create from Python
+d = Diagram(type="workflow", title="Analysis Pipeline")
+d.add_node("load", "Load Data", shape="stadium")
+d.add_node("proc", "Process", shape="rounded", emphasis="primary")
+d.add_node("save", "Save Results", shape="stadium", emphasis="success")
+d.add_edge("load", "proc")
+d.add_edge("proc", "save")
+
+# Export
+d.to_mermaid("pipeline.mmd")
+d.to_graphviz("pipeline.dot")
+
+# Or from YAML specification
+d = Diagram.from_yaml("pipeline.diagram.yaml")
+
+
+
+
+

YAML Specification

+
type: workflow
+title: SciTeX Analysis Pipeline
+
+paper:
+  column: single          # single or double
+  mode: publication       # draft or publication
+  reading_direction: left_to_right
+
+layout:
+  groups:
+    Input: [load, preprocess]
+    Analysis: [analyze, test]
+
+nodes:
+  - id: load
+    label: Load Data
+    shape: stadium
+  - id: analyze
+    label: Statistical Test
+    shape: rounded
+    emphasis: primary
+
+edges:
+  - from: load
+    to: analyze
+
+
+
+
+

Node Shapes

+

box, rounded, stadium, diamond, circle, codeblock

+
+
+

Node Emphasis

+
    +
  • normal – Default (dark)

  • +
  • primary – Key nodes (blue)

  • +
  • success – Positive outcomes (green)

  • +
  • warning – Issues (red)

  • +
  • muted – Secondary (gray)

  • +
+
+
+

Diagram Types

+
    +
  • workflow – Left-to-right sequential processes

  • +
  • decision – Top-to-bottom decision trees

  • +
  • pipeline – Data pipeline stages

  • +
  • hierarchy – Tree structures

  • +
  • comparison – Side-by-side comparison

  • +
+
+
+

Paper Modes

+
    +
  • draft – Full arrows, medium spacing, all edges visible

  • +
  • publication – Tight spacing, return edges invisible, optimized for column width

  • +
+
+
+

Backends

+
    +
  • Mermaid (.mmd) – Web/markdown rendering

  • +
  • Graphviz DOT (.dot) – Tighter layouts, render with dot -Tpng

  • +
  • YAML (.diagram.yaml) – Semantic spec, human/LLM-readable

  • +
+
+
+

API Reference

+

See scitex.diagram API Reference for the auto-generated Python API.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/dict.html b/src/scitex/_sphinx_html/modules/dict.html new file mode 100644 index 000000000..169999324 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/dict.html @@ -0,0 +1,681 @@ + + + + + + + + + dict Module (stx.dict) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

dict Module (stx.dict)

+

See scitex.dict API Reference for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/dsp.html b/src/scitex/_sphinx_html/modules/dsp.html new file mode 100644 index 000000000..1adfda81d --- /dev/null +++ b/src/scitex/_sphinx_html/modules/dsp.html @@ -0,0 +1,749 @@ + + + + + + + + + DSP Module (stx.dsp) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

DSP Module (stx.dsp)

+

Digital signal processing tools for filtering, spectral analysis, +and time-frequency decomposition.

+
+

Quick Reference

+
import scitex as stx
+import numpy as np
+
+# Generate test signal
+fs = 1000  # Hz
+t = np.arange(0, 1, 1/fs)
+signal = np.sin(2 * np.pi * 10 * t) + 0.5 * np.sin(2 * np.pi * 50 * t)
+
+# Filtering
+filtered = stx.dsp.filt.bandpass(signal, fs=fs, low=5, high=30)
+low_pass = stx.dsp.filt.lowpass(signal, fs=fs, cutoff=20)
+high_pass = stx.dsp.filt.highpass(signal, fs=fs, cutoff=5)
+
+# Power spectral density
+freqs, psd = stx.dsp.psd(signal, fs=fs)
+
+# Band powers
+powers = stx.dsp.band_powers(signal, fs=fs, bands={
+    "delta": (1, 4),
+    "theta": (4, 8),
+    "alpha": (8, 13),
+    "beta": (13, 30),
+})
+
+# Hilbert transform (analytic signal)
+analytic = stx.dsp.hilbert(signal)
+amplitude = np.abs(analytic)
+phase = np.angle(analytic)
+
+# Wavelet decomposition
+coeffs = stx.dsp.wavelet(signal, fs=fs, freqs=np.arange(1, 50))
+
+
+
+
+

Available Functions

+

Filtering (stx.dsp.filt)

+
    +
  • bandpass(signal, fs, low, high)

  • +
  • lowpass(signal, fs, cutoff)

  • +
  • highpass(signal, fs, cutoff)

  • +
  • notch(signal, fs, freq)

  • +
+

Spectral Analysis

+
    +
  • psd(signal, fs) – Power spectral density

  • +
  • band_powers(signal, fs, bands) – Power in frequency bands

  • +
  • wavelet(signal, fs, freqs) – Continuous wavelet transform

  • +
+

Time-Frequency

+
    +
  • hilbert(signal) – Analytic signal via Hilbert transform

  • +
  • pac(signal, fs) – Phase-amplitude coupling

  • +
+

Utilities

+
    +
  • demo_sig(fs, duration) – Generate demo signals for testing

  • +
  • crop(signal, start, end, fs) – Crop signal to time range

  • +
  • detect_ripples(signal, fs) – Detect sharp-wave ripples

  • +
+
+
+

API Reference

+

See scitex.dsp API Reference for the auto-generated Python API.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/etc.html b/src/scitex/_sphinx_html/modules/etc.html new file mode 100644 index 000000000..dc4786908 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/etc.html @@ -0,0 +1,681 @@ + + + + + + + + + etc Module (stx.etc) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

etc Module (stx.etc)

+

See /api/scitex.etc for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/events.html b/src/scitex/_sphinx_html/modules/events.html new file mode 100644 index 000000000..e556affd7 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/events.html @@ -0,0 +1,681 @@ + + + + + + + + + events Module (stx.events) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

events Module (stx.events)

+

See /api/scitex.events for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/gen.html b/src/scitex/_sphinx_html/modules/gen.html new file mode 100644 index 000000000..41552d4e1 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/gen.html @@ -0,0 +1,711 @@ + + + + + + + + + Gen Module (stx.gen) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Gen Module (stx.gen)

+

General utilities for path management, shell commands, and system operations.

+
+

Note

+

Session management has moved to @stx.session (see Core Concepts). +The scitex.gen.start() / scitex.gen.close() pattern is deprecated.

+
+
+

Quick Reference

+
import scitex as stx
+
+# Timestamped output directories
+path = stx.gen.mk_spath("./results")
+# → ./results/20260213_143022/
+
+# Run shell commands
+stx.gen.run("ls -la")
+
+# Clipboard operations
+stx.gen.copy("text to clipboard")
+text = stx.gen.paste()
+
+# String to valid path
+stx.gen.title2path("My Experiment #1")
+# → "my_experiment_1"
+
+
+
+
+

API Reference

+

See scitex.gen API Reference for the auto-generated Python API.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/gists.html b/src/scitex/_sphinx_html/modules/gists.html new file mode 100644 index 000000000..82fd01e55 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/gists.html @@ -0,0 +1,681 @@ + + + + + + + + + gists Module (stx.gists) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

gists Module (stx.gists)

+

See /api/scitex.gists for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/git.html b/src/scitex/_sphinx_html/modules/git.html new file mode 100644 index 000000000..9a066bca7 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/git.html @@ -0,0 +1,681 @@ + + + + + + + + + git Module (stx.git) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

git Module (stx.git)

+

See /api/scitex.git for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/index.html b/src/scitex/_sphinx_html/modules/index.html new file mode 100644 index 000000000..27283cf3c --- /dev/null +++ b/src/scitex/_sphinx_html/modules/index.html @@ -0,0 +1,946 @@ + + + + + + + + + Module Overview — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Module Overview

+

SciTeX is organized into focused modules. +All modules are accessible via import scitex as stx followed by stx.<module>.

+ + + + + + + + +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/introspect.html b/src/scitex/_sphinx_html/modules/introspect.html new file mode 100644 index 000000000..a7c86382a --- /dev/null +++ b/src/scitex/_sphinx_html/modules/introspect.html @@ -0,0 +1,746 @@ + + + + + + + + + Introspect Module (stx.introspect) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Introspect Module (stx.introspect)

+

IPython-like code inspection for exploring Python packages.

+
+

Quick Reference

+
import scitex as stx
+
+# Function signature (like IPython's func?)
+stx.introspect.q("scitex.stats.test_ttest_ind")
+# → name, signature, parameters, return type
+
+# Full source code (like IPython's func??)
+stx.introspect.qq("scitex.stats.test_ttest_ind")
+# → complete source with line numbers
+
+# List module members (like enhanced dir())
+stx.introspect.dir("scitex.plt", filter="public", kind="functions")
+
+# Recursive API tree
+df = stx.introspect.list_api("scitex", max_depth=2)
+
+
+
+
+

IPython-Style Shortcuts

+
    +
  • q(dotted_path) – Signature and parameters (like func?)

  • +
  • qq(dotted_path) – Full source code (like func??)

  • +
  • dir(dotted_path, filter, kind) – List members with filtering

  • +
+

Filters: "public", "private", "dunder", "all"

+

Kinds: "functions", "classes", "data", "modules"

+
+
+

Documentation

+
    +
  • get_docstring(path, format) – Extract docstrings ("raw", "parsed", "summary")

  • +
  • get_exports(path) – Get __all__ exports

  • +
  • find_examples(path) – Find usage examples in tests/examples

  • +
+
+
+

Type Analysis

+
    +
  • get_type_hints_detailed(path) – Full type annotation analysis

  • +
  • get_class_hierarchy(path) – Inheritance tree (MRO + subclasses)

  • +
  • get_class_annotations(path) – Class variable annotations

  • +
+
+
+

Code Analysis

+
    +
  • get_imports(path, categorize) – All imports (AST-based, grouped by stdlib/third-party/local)

  • +
  • get_dependencies(path, recursive) – Module dependency tree

  • +
  • get_call_graph(path, max_depth) – Function call graph (with timeout protection)

  • +
+
+
+

API Tree

+
# Generate full module tree as DataFrame
+df = stx.introspect.list_api("scitex", max_depth=3, docstring=True)
+
+
+
+
+

API Reference

+

See scitex.introspect API Reference for the auto-generated Python API.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/io.html b/src/scitex/_sphinx_html/modules/io.html new file mode 100644 index 000000000..ad3e91ca9 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/io.html @@ -0,0 +1,803 @@ + + + + + + + + + IO Module (stx.io) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

IO Module (stx.io)

+

Universal file I/O through stx.io.save() and stx.io.load(). +Format is determined by file extension.

+
+

Quick Reference

+
import scitex as stx
+
+# Save
+stx.io.save(data, "output.csv")       # DataFrame → CSV
+stx.io.save(arr, "output.npy")        # ndarray → NumPy
+stx.io.save(fig, "output.png")        # Figure → PNG + CSV + YAML recipe
+stx.io.save(obj, "output.pkl")        # any object → pickle
+stx.io.save(d, "output.yaml")         # dict → YAML
+stx.io.save(d, "output.json")         # dict → JSON
+
+# Load
+data = stx.io.load("input.csv")       # → DataFrame
+arr = stx.io.load("input.npy")        # → ndarray
+obj = stx.io.load("input.pkl")        # → original object
+d = stx.io.load("input.yaml")         # → dict
+
+
+
+
+

Supported Formats

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Extension

Save Type

Load Return

.csv

DataFrame, dict, ndarray

DataFrame

.npy

ndarray

ndarray

.npz

dict of ndarrays

NpzFile

.pkl / .pickle

any Python object

original object

.yaml / .yml

dict

dict

.json

dict, list

dict or list

.png / .jpg / .svg / .pdf / .tiff

matplotlib Figure

ndarray (image) or Figure

.hdf5 / .h5

dict, ndarray

dict or ndarray

.mat

dict

dict

.pth / .pt

PyTorch state_dict

dict

.parquet

DataFrame

DataFrame

.txt

str

str

+
+
+

Figure Saving

+

When saving a matplotlib Figure, stx.io.save automatically:

+
    +
  1. Saves the image file (.png, .svg, etc.)

  2. +
  3. Exports a .csv with the plotted data extracted from axes

  4. +
  5. Exports a .yaml recipe for reproducing the figure

  6. +
+
fig, ax = stx.plt.subplots()
+ax.plot_line(x, y, label="signal")
+stx.io.save(fig, "results/plot.png")
+# Creates:
+#   results/plot.png       (the figure)
+#   results/plot.csv       (x, y data)
+#   results/plot.yaml      (recipe for stx.plt.reproduce)
+
+
+
+
+

Provenance Tracking

+

Inside @stx.session, every save and load records file hashes +to a local SQLite database for reproducibility verification.

+
@stx.session
+def main(logger=stx.INJECTED, **kw):
+    data = stx.io.load("input.csv")    # hash recorded as input
+    stx.io.save(result, "output.csv")   # hash recorded as output
+
+
+
+
+

Other Functions

+
    +
  • stx.io.glob(pattern) – Find files matching a glob pattern

  • +
  • stx.io.load_configs(config_dir) – Load and merge YAML config files

  • +
+
+
+

API Reference

+

See scitex.io API Reference for the auto-generated Python API.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/linalg.html b/src/scitex/_sphinx_html/modules/linalg.html new file mode 100644 index 000000000..050804b4c --- /dev/null +++ b/src/scitex/_sphinx_html/modules/linalg.html @@ -0,0 +1,681 @@ + + + + + + + + + linalg Module (stx.linalg) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

linalg Module (stx.linalg)

+

See /api/scitex.linalg for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/linter.html b/src/scitex/_sphinx_html/modules/linter.html new file mode 100644 index 000000000..3d8eb940e --- /dev/null +++ b/src/scitex/_sphinx_html/modules/linter.html @@ -0,0 +1,802 @@ + + + + + + + + + Linter Module (stx.linter) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Linter Module (stx.linter)

+

AST-based Python linter enforcing SciTeX conventions for reproducible +scientific code.

+
+

Note

+

stx.linter wraps the standalone +scitex-linter package. +Install with: pip install scitex-linter.

+
+
+

Quick Reference

+
# Check a file
+scitex linter check my_script.py
+
+# Check with specific severity
+scitex linter check my_script.py --severity warning
+
+# List all rules
+scitex linter list-rules
+
+# Filter by category
+scitex linter list-rules --category session
+
+
+
from scitex_linter import check_file, list_rules
+
+# Check a file
+results = check_file("my_script.py")
+
+# List available rules
+rules = list_rules()
+
+
+
+
+

Rule Categories

+

45 rules across 8 categories:

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Prefix

Category

Examples

STX-S

Session

Missing @stx.session, missing if __name__ guard, missing return

STX-I

Import

Using import mngs instead of import scitex, bare import numpy

STX-IO

I/O

Using open() instead of stx.io, hardcoded paths

STX-P

Plotting

Using plt.show() instead of stx.io.save, missing set_xyt

STX-ST

Statistics

Using scipy.stats directly instead of stx.stats

STX-PA

Path

Hardcoded absolute paths, missing stx.gen.mk_spath

STX-FM

Format

Non-snake_case names, magic numbers, missing docstrings

+
+
+

Severity Levels

+
    +
  • error – Must fix (breaks reproducibility or correctness)

  • +
  • warning – Should fix (best practice violations)

  • +
  • info – Suggestions for improvement

  • +
+
+
+

Interfaces

+

The linter is available through multiple interfaces:

+

CLI:

+
scitex linter check script.py
+scitex-linter check script.py    # standalone CLI
+
+
+

Python API:

+
from scitex_linter import check_file, check_source
+
+results = check_file("script.py")
+results = check_source("import numpy as np\n...")
+
+
+

Flake8 Plugin:

+
flake8 --select=STX script.py
+
+
+

MCP Tools:

+
# Available as MCP tools for AI agents
+linter_check(path="script.py", severity="warning")
+linter_list_rules(category="session")
+linter_check_source(source="...", filepath="<stdin>")
+
+
+
+
+

Configuration

+

Configure via pyproject.toml:

+
[tool.scitex-linter]
+severity = "warning"
+ignore = ["STX-S002", "STX-FM001"]
+
+
+
+
+

API Reference

+

See scitex.linter for the auto-generated Python API.

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/logging.html b/src/scitex/_sphinx_html/modules/logging.html new file mode 100644 index 000000000..dec7a1dde --- /dev/null +++ b/src/scitex/_sphinx_html/modules/logging.html @@ -0,0 +1,795 @@ + + + + + + + + + Logging Module (stx.logging) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Logging Module (stx.logging)

+

Unified logging with file/console output, custom warnings, exception +hierarchy, and stream redirection.

+
+

Quick Reference

+
import scitex as stx
+from scitex import logging
+
+# Get a logger
+logger = logging.getLogger(__name__)
+logger.info("Processing data")
+logger.success("Analysis complete")   # Custom level (31)
+logger.fail("Model diverged")         # Custom level (35)
+
+# Configure globally
+logging.configure(level="DEBUG", log_file="./run.log")
+logging.set_level("WARNING")
+
+# Temporary file logging
+with logging.log_to_file("analysis.log"):
+    logger.info("This goes to both console and file")
+
+# Warnings
+logging.warn("Large dataset", category=logging.PerformanceWarning)
+logging.warn_deprecated("old_func", "new_func", version="3.0")
+logging.filterwarnings("ignore", category=logging.UnitWarning)
+
+
+
+
+

Log Levels

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Level

Value

Description

DEBUG

10

Detailed diagnostic information

INFO

20

General operational messages

WARNING

30

Something unexpected but not fatal

SUCCESS

31

Custom: operation completed successfully

FAIL

35

Custom: operation failed (non-fatal)

ERROR

40

Serious problem

CRITICAL

50

Program may not continue

+
+
+

Warning Categories

+
    +
  • SciTeXWarning – Base warning class

  • +
  • UnitWarning – SI unit convention issues

  • +
  • StyleWarning – Formatting issues

  • +
  • SciTeXDeprecationWarning – Deprecated features

  • +
  • PerformanceWarning – Performance issues

  • +
  • DataLossWarning – Potential data loss

  • +
+
+
+

Exception Hierarchy

+

All inherit from SciTeXError:

+
    +
  • I/O: IOError, FileFormatError, SaveError, LoadError

  • +
  • Config: ConfigurationError, ConfigFileNotFoundError, ConfigKeyError

  • +
  • Path: PathError, InvalidPathError, PathNotFoundError

  • +
  • Data: DataError, ShapeError, DTypeError

  • +
  • Plotting: PlottingError, FigureNotFoundError, AxisError

  • +
  • Stats: StatsError, TestError

  • +
  • Scholar: ScholarError, SearchError, PDFDownloadError, DOIResolutionError

  • +
  • NN: NNError, ModelError

  • +
  • Template: TemplateError, TemplateViolationError

  • +
+
+
+

Stream Redirection

+
from scitex.logging import tee
+import sys
+
+# Redirect stdout/stderr to log files
+sys.stdout, sys.stderr = tee(sys, sdir="./output")
+
+
+

The @stx.session decorator handles this automatically.

+
+
+

API Reference

+

See scitex.logging API Reference for the auto-generated Python API.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/media.html b/src/scitex/_sphinx_html/modules/media.html new file mode 100644 index 000000000..f41af4de4 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/media.html @@ -0,0 +1,681 @@ + + + + + + + + + media Module (stx.media) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

media Module (stx.media)

+

See /api/scitex.media for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/module.html b/src/scitex/_sphinx_html/modules/module.html new file mode 100644 index 000000000..a25df5f31 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/module.html @@ -0,0 +1,681 @@ + + + + + + + + + module Module (stx.module) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

module Module (stx.module)

+

See /api/scitex.module for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/msword.html b/src/scitex/_sphinx_html/modules/msword.html new file mode 100644 index 000000000..a11d138b3 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/msword.html @@ -0,0 +1,681 @@ + + + + + + + + + msword Module (stx.msword) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

msword Module (stx.msword)

+

See /api/scitex.msword for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/nn.html b/src/scitex/_sphinx_html/modules/nn.html new file mode 100644 index 000000000..d1fd86ad4 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/nn.html @@ -0,0 +1,757 @@ + + + + + + + + + NN Module (stx.nn) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

NN Module (stx.nn)

+

PyTorch neural network layers for signal processing and neuroscience +applications.

+
+

Quick Reference

+
import scitex as stx
+import torch
+
+# Bandpass filtering as a differentiable layer
+bpf = stx.nn.BandPassFilter(
+    bands=[[4, 8], [8, 13], [13, 30]],  # theta, alpha, beta
+    fs=256, seq_len=1024
+)
+filtered = bpf(signal)  # (batch, channels, 3, 1024)
+
+# Power spectral density
+psd = stx.nn.PSD(sample_rate=256)
+power, freqs = psd(signal)
+
+# Phase-amplitude coupling
+pac = stx.nn.PAC(seq_len=1024, fs=256)
+coupling = pac(signal)
+
+
+
+
+

Signal Processing Layers

+

Filtering (all differentiable):

+
    +
  • BandPassFilter(bands, fs, seq_len) – Multi-band frequency filtering

  • +
  • BandStopFilter(bands, fs, seq_len) – Reject frequency bands

  • +
  • LowPassFilter(cutoffs_hz, fs, seq_len) – Anti-aliasing / smoothing

  • +
  • HighPassFilter(cutoffs_hz, fs, seq_len) – High-frequency emphasis

  • +
  • GaussianFilter(sigma) – Gaussian kernel smoothing

  • +
  • DifferentiableBandPassFilter(...) – Learnable bandpass parameters

  • +
+

Spectral Analysis:

+
    +
  • Spectrogram(sampling_rate, n_fft) – STFT-based magnitude spectrogram

  • +
  • PSD(sample_rate, prob) – FFT-based power spectral density

  • +
  • Wavelet(samp_rate, freq_scale) – Continuous wavelet transform

  • +
+

Phase & Coupling:

+
    +
  • Hilbert(seq_len) – Analytic signal (phase + amplitude)

  • +
  • ModulationIndex(n_bins) – Phase-amplitude coupling metric

  • +
  • PAC(seq_len, fs, ...) – Complete PAC analysis pipeline

  • +
+
+
+

Channel Manipulation

+
    +
  • SwapChannels() – Random channel permutation (training augmentation)

  • +
  • DropoutChannels(dropout) – Drop entire channels

  • +
  • ChannelGainChanger(n_chs) – Learnable per-channel scaling

  • +
  • FreqGainChanger(n_bands, fs) – Learnable per-band scaling

  • +
+
+
+

Attention & Shape

+
    +
  • SpatialAttention(n_chs_in) – Adaptive channel weighting

  • +
  • TransposeLayer(axis1, axis2) – Dimension permutation

  • +
  • AxiswiseDropout(dropout_prob, dim) – Drop entire axis

  • +
+
+
+

Architectures

+
    +
  • ResNet1D(n_chs, n_out, n_blks) – 1D residual network

  • +
  • BNet / BNet_Res – Multi-head EEG classifier

  • +
  • MNet1000 – 2D CNN feature extractor

  • +
+
+
+

API Reference

+

See scitex.nn API Reference for the auto-generated Python API.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/notebook.html b/src/scitex/_sphinx_html/modules/notebook.html new file mode 100644 index 000000000..7e16fac04 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/notebook.html @@ -0,0 +1,681 @@ + + + + + + + + + notebook Module (stx.notebook) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

notebook Module (stx.notebook)

+

See /api/scitex.notebook for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/notify.html b/src/scitex/_sphinx_html/modules/notify.html new file mode 100644 index 000000000..760aa1039 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/notify.html @@ -0,0 +1,681 @@ + + + + + + + + + notify Module (stx.notify) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

notify Module (stx.notify)

+

See /api/scitex.notify for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/os.html b/src/scitex/_sphinx_html/modules/os.html new file mode 100644 index 000000000..caa75a462 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/os.html @@ -0,0 +1,681 @@ + + + + + + + + + os Module (stx.os) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

os Module (stx.os)

+

See /api/scitex.os for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/parallel.html b/src/scitex/_sphinx_html/modules/parallel.html new file mode 100644 index 000000000..0112e810b --- /dev/null +++ b/src/scitex/_sphinx_html/modules/parallel.html @@ -0,0 +1,681 @@ + + + + + + + + + parallel Module (stx.parallel) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

parallel Module (stx.parallel)

+

See /api/scitex.parallel for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/path.html b/src/scitex/_sphinx_html/modules/path.html new file mode 100644 index 000000000..3a026291b --- /dev/null +++ b/src/scitex/_sphinx_html/modules/path.html @@ -0,0 +1,681 @@ + + + + + + + + + path Module (stx.path) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

path Module (stx.path)

+

See scitex.path API Reference for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/pd.html b/src/scitex/_sphinx_html/modules/pd.html new file mode 100644 index 000000000..05e14ab26 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/pd.html @@ -0,0 +1,721 @@ + + + + + + + + + PD Module (stx.pd) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

PD Module (stx.pd)

+

Pandas DataFrame helper functions for common transformations.

+
+

Quick Reference

+
import scitex as stx
+import pandas as pd
+
+df = pd.DataFrame({
+    "subject": ["A", "A", "B", "B"],
+    "condition": ["ctrl", "exp", "ctrl", "exp"],
+    "score": [10, 15, 12, 18],
+})
+
+# Find individual (unique) values
+subjects = stx.pd.find_indi(df, "subject")
+
+# Convert to long format
+melted = stx.pd.melt_cols(df, id_vars=["subject"])
+
+# Merge columns
+merged = stx.pd.merge_cols(df, cols=["subject", "condition"], sep="_")
+
+# Force to DataFrame
+stx.pd.force_df({"a": [1, 2], "b": [3, 4]})
+
+
+
+
+

Available Functions

+
    +
  • find_indi(df, col) – Find unique individual identifiers

  • +
  • find_pval(df) – Find p-value columns in a DataFrame

  • +
  • force_df(data) – Convert any data to a DataFrame

  • +
  • melt_cols(df, id_vars) – Melt columns to long format

  • +
  • merge_cols(df, cols, sep) – Merge multiple columns into one

  • +
  • to_xyz(df, x, y, z) – Reshape to x, y, z pivot format

  • +
+
+
+

API Reference

+

See scitex.pd API Reference for the auto-generated Python API.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/plt.html b/src/scitex/_sphinx_html/modules/plt.html new file mode 100644 index 000000000..11829ed68 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/plt.html @@ -0,0 +1,838 @@ + + + + + + + + + PLT Module (stx.plt) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

PLT Module (stx.plt)

+

Publication-ready figures powered by figrecipe.

+
+

Note

+

stx.plt wraps matplotlib via the figrecipe package, adding +mm-based layout, data-tracking axes, auto CSV export, and reproducible +YAML recipes. The standalone package can also be used directly: +pip install figrecipe.

+
+
+

Quick Reference

+
import scitex as stx
+
+# Create figure (returns FigWrapper, AxisWrapper)
+fig, ax = stx.plt.subplots()
+fig, axes = stx.plt.subplots(2, 2)
+
+# Data-tracking plot methods (all record data for CSV export)
+ax.plot_line(x, y, label="signal")
+ax.plot_scatter(x, y, alpha=0.5)
+ax.plot_bar(labels, values)
+ax.plot_heatmap(matrix)
+ax.plot_violin(groups)
+ax.plot_box(groups)
+ax.plot_kde(data)
+
+# Axis helper (xlabel, ylabel, title in one call)
+ax.set_xyt("Time (s)", "Amplitude (mV)", "EEG Signal")
+
+# Save (auto-exports CSV + YAML recipe)
+stx.io.save(fig, "figure.png")
+
+
+
+
+

Plot Types (47)

+

All ax.plot_* methods track their input data so it can be exported as CSV.

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Category

Types

Line & Curve

plot, step, fill, fill_between, fill_betweenx, errorbar, stackplot, stairs

Scatter & Points

scatter

Bar & Categorical

bar, barh

Distribution

hist, hist2d, boxplot, violinplot, ecdf

2D Image & Matrix

imshow, matshow, pcolor, pcolormesh, hexbin, spy

Contour & Surface

contour, contourf, tricontour, tricontourf, tripcolor, triplot

Spectral & Signal

specgram, psd, csd, cohere, angle_spectrum, magnitude_spectrum, phase_spectrum, acorr, xcorr

Vector & Flow

quiver, barbs, streamplot

Special

pie, stem, eventplot, loglog, semilogx, semilogy, graph

+

Standard matplotlib methods (ax.plot, ax.scatter, etc.) also work +and are tracked.

+
+
+

MM-Based Layout

+

Figures use millimeter dimensions for precise control:

+
fig, ax = stx.plt.subplots(
+    axes_width_mm=80,
+    axes_height_mm=60,
+)
+
+# Multi-panel with exact spacing
+fig, axes = stx.plt.subplots(
+    nrows=2, ncols=3,
+    axes_width_mm=50,
+    axes_height_mm=40,
+)
+
+
+
+
+

Axis Helpers

+
# Set xlabel, ylabel, title in one call
+ax.set_xyt("X Label", "Y Label", "Title")
+
+# Automatic axis label formatting with units
+ax.set_xyt("Frequency (Hz)", "Power (dB)", "Power Spectrum")
+
+
+
+
+

Figure Composition

+

Combine multiple figures into panels:

+
# Compose panels A, B, C
+stx.plt.compose(
+    sources=["fig_a.png", "fig_b.png", "fig_c.png"],
+    output_path="combined.png",
+    layout="horizontal",
+    panel_labels=True,
+)
+
+
+
+
+

Statistical Annotations

+

Add significance brackets to figures:

+
# In the declarative spec
+stat_annotations:
+  - x1: 0
+    x2: 1
+    y: 5.5
+    text: "***"
+  - x1: 0
+    x2: 2
+    y: 6.5
+    p_value: 0.02
+    style: stars
+
+
+
+
+

Reproducible Figures

+

When saved via stx.io.save, figures get a .yaml recipe. +Reproduce any figure from its recipe:

+
stx.plt.reproduce("figure.yaml", output_path="reproduced.png")
+
+
+
+
+

Style Presets

+
import figrecipe as fr
+
+fr.load_style("SCITEX")       # Publication defaults
+fr.load_style("MATPLOTLIB")   # Standard matplotlib
+
+
+

See the Plot Gallery for visual examples.

+
+
+

API Reference

+

See scitex.plt API Reference for the auto-generated Python API.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/project.html b/src/scitex/_sphinx_html/modules/project.html new file mode 100644 index 000000000..dbf77acaf --- /dev/null +++ b/src/scitex/_sphinx_html/modules/project.html @@ -0,0 +1,681 @@ + + + + + + + + + project Module (stx.project) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

project Module (stx.project)

+

See /api/scitex.project for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/repro.html b/src/scitex/_sphinx_html/modules/repro.html new file mode 100644 index 000000000..71eb82b30 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/repro.html @@ -0,0 +1,743 @@ + + + + + + + + + Repro Module (stx.repro) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Repro Module (stx.repro)

+

Reproducibility utilities: random state management, ID generation, +timestamps, and array hashing.

+
+

Quick Reference

+
import scitex as stx
+
+# Fix all random seeds (numpy, torch, random, ...)
+rng = stx.repro.get()           # Global manager (seed=42)
+rng = stx.repro.reset(seed=123) # Reset with new seed
+
+# Named generators for deterministic results
+data_gen = rng.get_np_generator("data")
+data = data_gen.random(100)  # Same seed+name = same result
+
+# Unique identifiers
+stx.repro.gen_id()
+# → "2026Y-02M-13D-14h30m15s_a3Bc9xY2"
+
+stx.repro.gen_timestamp()
+# → "2026-0213-1430"
+
+# Verify reproducibility
+rng.verify(data, "train_data")  # First: caches hash
+rng.verify(data, "train_data")  # Later: verifies match
+
+
+
+
+

RandomStateManager

+

Central class for managing random states across libraries.

+
rng = stx.repro.RandomStateManager(seed=42)
+
+# Named generators (same name + seed = deterministic)
+np_gen = rng.get_np_generator("experiment")
+torch_gen = rng.get_torch_generator("model")
+
+# Checkpoint and restore
+rng.checkpoint("before_training")
+rng.restore("before_training.pkl")
+
+# Temporary seed change
+with rng.temporary_seed(999):
+    noise = rng.get_np_generator("noise").random(10)
+
+
+

Automatically fixes seeds for: random, numpy, torch (+ CUDA), +tensorflow, jax.

+
+
+

Available Functions

+
    +
  • get(verbose) – Get or create global RandomStateManager singleton

  • +
  • reset(seed, verbose) – Reset global instance with new seed

  • +
  • fix_seeds(seed, ...) – Legacy function (use RandomStateManager instead)

  • +
  • gen_id(time_format, N) – Generate unique timestamp + random ID

  • +
  • gen_timestamp() – Generate timestamp string for file naming

  • +
  • hash_array(array_data) – SHA256 hash of numpy array (16 chars)

  • +
+
+
+

API Reference

+

See scitex.repro API Reference for the auto-generated Python API.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/resource.html b/src/scitex/_sphinx_html/modules/resource.html new file mode 100644 index 000000000..a5bc7db46 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/resource.html @@ -0,0 +1,681 @@ + + + + + + + + + resource Module (stx.resource) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

resource Module (stx.resource)

+

See /api/scitex.resource for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/rng.html b/src/scitex/_sphinx_html/modules/rng.html new file mode 100644 index 000000000..b2d30b22a --- /dev/null +++ b/src/scitex/_sphinx_html/modules/rng.html @@ -0,0 +1,681 @@ + + + + + + + + + rng Module (stx.rng) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

rng Module (stx.rng)

+

See /api/scitex.rng for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/schema.html b/src/scitex/_sphinx_html/modules/schema.html new file mode 100644 index 000000000..b25f5360d --- /dev/null +++ b/src/scitex/_sphinx_html/modules/schema.html @@ -0,0 +1,681 @@ + + + + + + + + + schema Module (stx.schema) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

schema Module (stx.schema)

+

See /api/scitex.schema for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/scholar.html b/src/scitex/_sphinx_html/modules/scholar.html new file mode 100644 index 000000000..b65443bd8 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/scholar.html @@ -0,0 +1,801 @@ + + + + + + + + + Scholar Module (stx.scholar) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Scholar Module (stx.scholar)

+

Literature management: search papers, download PDFs, enrich BibTeX, +and organize a local library across multiple projects.

+
+

Quick Reference

+
from scitex.scholar import Scholar
+
+scholar = Scholar(project="my_research")
+
+# Load and enrich BibTeX
+papers = scholar.load_bibtex("references.bib")
+enriched = scholar.enrich_papers(papers)
+# Adds: DOIs, abstracts, citation counts, impact factors
+
+# Save to library and export
+scholar.save_papers_to_library(enriched)
+scholar.save_papers_as_bibtex(enriched, "enriched.bib")
+
+# Search your library
+results = scholar.search_library("neural oscillations")
+
+# Download PDFs
+scholar.download_pdfs(dois, output_dir)
+
+
+
+
+

CLI Usage

+
# Full pipeline from BibTeX
+scitex scholar bibtex refs.bib --project myresearch --num-workers 8
+
+# Search papers
+scitex scholar search "deep learning EEG"
+
+# Download PDFs
+scitex scholar download --doi 10.1038/nature12373
+
+# Institutional authentication
+scitex scholar auth --method openathens
+scitex scholar auth --method shibboleth --institution "MIT"
+
+
+
+
+

Data Sources

+

Searches and enriches from:

+
    +
  • CrossRef (167M+ papers) – DOI resolution, citation counts

  • +
  • Semantic Scholar – Abstracts, references, influence scores

  • +
  • PubMed – Biomedical literature

  • +
  • arXiv – Preprints

  • +
  • OpenAlex (284M+ works) – Open metadata

  • +
+
+
+

Key Classes

+
    +
  • Scholar – Main entry point (search, enrich, download, organize)

  • +
  • Paper – Type-safe metadata container (Pydantic model)

  • +
  • Papers – Collection with filtering, sorting, and export

  • +
  • ScholarConfig – YAML-based configuration

  • +
  • ScholarLibrary – Local library storage and caching

  • +
+
+
+

Paper Metadata

+

Each Paper contains structured metadata sections:

+
paper.metadata.basic          # title, authors, year, abstract, keywords
+paper.metadata.id             # DOI, arXiv, PMID, Semantic Scholar ID
+paper.metadata.publication    # journal, impact factor, volume, issue
+paper.metadata.citation_count # total + yearly breakdown (2015--2024)
+paper.metadata.url            # DOI URL, publisher, arXiv, PDFs
+paper.metadata.access         # open access status, license
+
+
+
+
+

Filtering and Sorting

+
# Criteria-based filtering
+recent = papers.filter(year_min=2020, has_doi=True)
+elite = papers.filter(min_impact_factor=10, min_citations=500)
+
+# Lambda filtering
+custom = papers.filter(lambda p: "EEG" in (p.metadata.basic.title or ""))
+
+# Sorting
+papers.sort_by("year", reverse=True)
+papers.sort_by("citation_count", reverse=True)
+
+# Chaining
+top_recent = papers.filter(year_min=2020).sort_by("citation_count", reverse=True)
+
+
+
+
+

Project Organization

+
scholar = Scholar(project="review_paper")
+scholar.list_projects()
+papers = scholar.load_project()
+
+# Export to multiple formats
+scholar.save_papers_as_bibtex(papers, "output.bib")
+papers.to_dataframe()  # pandas DataFrame
+
+
+
+
+

Storage Architecture

+
~/.scitex/scholar/library/
++-- MASTER/                     # Centralized master storage
+|   +-- 8DIGIT01/              # Hash-based unique ID from DOI
+|   |   +-- metadata.json
+|   |   +-- paper.pdf
++-- project_name/               # Project-specific symlinks
+    +-- Author-Year-Journal -> ../MASTER/8DIGIT01
+
+
+
+
+

API Reference

+

See scitex.scholar API Reference for the auto-generated Python API.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/security.html b/src/scitex/_sphinx_html/modules/security.html new file mode 100644 index 000000000..0daef4497 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/security.html @@ -0,0 +1,681 @@ + + + + + + + + + security Module (stx.security) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

security Module (stx.security)

+

See /api/scitex.security for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/session.html b/src/scitex/_sphinx_html/modules/session.html new file mode 100644 index 000000000..7f6f2f044 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/session.html @@ -0,0 +1,890 @@ + + + + + + + + + Session Decorator (@stx.session) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Session Decorator (@stx.session)

+

The @stx.session decorator is the primary entry point for SciTeX +scripts. It wraps a function to provide automatic CLI generation, +output directory management, configuration injection, and provenance tracking.

+
+

Basic Usage

+
import scitex as stx
+
+@stx.session
+def main(
+    data_path="data.csv",       # CLI: --data-path data.csv
+    threshold=0.5,              # CLI: --threshold 0.7
+    CONFIG=stx.INJECTED,        # Auto-injected session config
+    plt=stx.INJECTED,           # Pre-configured matplotlib
+    logger=stx.INJECTED,        # Session logger
+):
+    """Analyze experimental data."""
+    data = stx.io.load(data_path)
+    result = process(data, threshold)
+    stx.io.save(result, "output.csv")
+    return 0  # exit code (0 = success)
+
+if __name__ == "__main__":
+    main()  # No arguments triggers CLI mode
+
+
+

Run from the command line:

+
python my_script.py --data-path experiment.csv --threshold 0.3
+python my_script.py --help  # Shows all parameters with defaults
+
+
+
+
+

How It Works

+Session lifecycle: main() → Parse CLI → Create dir → Load config → Execute → SUCCESS or ERROR + +

When main() is called without arguments, the decorator:

+
    +
  1. Parses CLI arguments from the function signature (type hints and defaults become argparse options)

  2. +
  3. Creates a session directory under script_out/RUNNING/{session_id}/

  4. +
  5. Loads configuration from ./config/*.yaml files into CONFIG

  6. +
  7. Injects globals (CONFIG, plt, COLORS, logger, rngg) into the function

  8. +
  9. Redirects stdout/stderr to log files

  10. +
  11. Executes the function with parsed arguments

  12. +
  13. Moves output from RUNNING/ to FINISHED_SUCCESS/ (or FINISHED_ERROR/)

  14. +
+

When called with arguments (e.g., main(data_path="x.csv")), the decorator +is bypassed and the function runs directly – useful for testing and notebooks.

+
+
+

Output Directory Structure

+

Each run produces a self-contained output directory:

+
my_script_out/
++-- FINISHED_SUCCESS/
+|   +-- 2026Y-02M-13D-14h30m15s_Z5MR/
+|       +-- CONFIGS/
+|       |   +-- CONFIG.yaml     # Frozen configuration
+|       |   +-- CONFIG.pkl      # Pickled config
+|       +-- logs/
+|       |   +-- stdout.log      # Captured stdout
+|       |   +-- stderr.log      # Captured stderr
+|       +-- output.csv          # Your saved files
+|       +-- sine.png            # Your saved figures
+|       +-- sine.csv            # Auto-exported figure data
++-- FINISHED_ERROR/
+|   +-- ...                     # Runs that returned non-zero
++-- RUNNING/
+    +-- ...                     # Currently active sessions
+
+
+

The session ID format is YYYY-MM-DD-HH:MM:SS_XXXX where XXXX is +a random 4-character suffix for uniqueness.

+
+
+

Injected Parameters

+

Use stx.INJECTED as the default value to receive auto-injected objects:

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Parameter Name

Type

Description

CONFIG

DotDict

Session config with ID, SDIR_RUN, FILE, ARGS, plus all ./config/*.yaml values

plt

module

matplotlib.pyplot configured for the session (Agg backend, style settings)

COLORS

DotDict

Color palette for consistent plotting

logger

Logger

SciTeX logger writing to session log files

rngg

RandomStateManager

Reproducibility manager (seeds fixed by default)

+

Only request the parameters you need:

+
@stx.session
+def main(n=100, CONFIG=stx.INJECTED):
+    """Minimal example -- only CONFIG injected."""
+    print(f"Session ID: {CONFIG.ID}")
+    print(f"Output dir: {CONFIG.SDIR_RUN}")
+    return 0
+
+
+
+
+

CONFIG Object

+

The injected CONFIG is a DotDict supporting both dictionary and +dot-notation access:

+
CONFIG["MODEL"]["hidden_size"]   # dict-style
+CONFIG.MODEL.hidden_size         # dot-style (equivalent)
+
+
+

It contains:

+
CONFIG.ID             # "2026Y-02M-13D-14h30m15s_Z5MR"
+CONFIG.PID            # Process ID
+CONFIG.FILE           # Path to the script
+CONFIG.SDIR_OUT       # Base output directory
+CONFIG.SDIR_RUN       # Current session's output directory
+CONFIG.START_DATETIME # When the session started
+CONFIG.ARGS           # Parsed CLI arguments as dict
+CONFIG.EXIT_STATUS    # 0 (success), 1 (error), or None
+
+
+

Any YAML files in ./config/ are merged into CONFIG:

+
# ./config/EXPERIMENT.yaml
+learning_rate: 0.001
+batch_size: 32
+
+
+
# Accessible as:
+CONFIG.EXPERIMENT.learning_rate  # 0.001
+CONFIG.EXPERIMENT.batch_size     # 32
+
+
+
+
+

Decorator Options

+
@stx.session(verbose=True, agg=False, notify=True, sdir_suffix="v2")
+def main(...):
+    ...
+
+
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Option

Type

Default

Description

verbose

bool

False

Enable verbose logging

agg

bool

True

Use matplotlib Agg backend (set False for interactive plots)

notify

bool

False

Send notification when session completes

sdir_suffix

str

None

Append suffix to output directory name

+
+
+

Best Practices

+
    +
  1. One session per script – each .py file should have one @stx.session function

  2. +
  3. Return 0 for success – the return value becomes the exit status

  4. +
  5. Save outputs with stx.io.save – files go into the session directory and are provenance-tracked

  6. +
  7. Put config in YAML – use ./config/*.yaml instead of hardcoding parameters

  8. +
  9. Use stx.repro.fix_seeds() – already called by default via rngg

  10. +
+
+
+

API Reference

+

See scitex.session API Reference for the auto-generated Python API.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/sh.html b/src/scitex/_sphinx_html/modules/sh.html new file mode 100644 index 000000000..22e39316d --- /dev/null +++ b/src/scitex/_sphinx_html/modules/sh.html @@ -0,0 +1,681 @@ + + + + + + + + + sh Module (stx.sh) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

sh Module (stx.sh)

+

See /api/scitex.sh for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/social.html b/src/scitex/_sphinx_html/modules/social.html new file mode 100644 index 000000000..90a7cece7 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/social.html @@ -0,0 +1,681 @@ + + + + + + + + + social Module (stx.social) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

social Module (stx.social)

+

See scitex.social API Reference for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/stats.html b/src/scitex/_sphinx_html/modules/stats.html new file mode 100644 index 000000000..7b2b03668 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/stats.html @@ -0,0 +1,820 @@ + + + + + + + + + Stats Module (stx.stats) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Stats Module (stx.stats)

+

23 statistical tests with effect sizes, confidence intervals, and +publication-ready formatting.

+
+

Quick Reference

+
import scitex as stx
+import numpy as np
+
+g1 = np.random.normal(0, 1, 50)
+g2 = np.random.normal(0.5, 1, 50)
+
+# Run a test
+result = stx.stats.test_ttest_ind(g1, g2)
+
+# Result is a flat dict with all details
+print(result["statistic"])     # t-statistic
+print(result["pvalue"])        # p-value
+print(result["effect_size"])   # Cohen's d
+print(result["stars"])         # Significance stars
+
+# Get as DataFrame or LaTeX
+df = stx.stats.test_ttest_ind(g1, g2, return_as="dataframe")
+tex = stx.stats.test_ttest_ind(g1, g2, return_as="latex")
+
+
+
+
+

Available Tests

+

Parametric

+
    +
  • test_ttest_1samp(data, popmean) – One-sample t-test

  • +
  • test_ttest_ind(g1, g2) – Independent two-sample t-test

  • +
  • test_ttest_rel(g1, g2) – Paired t-test

  • +
  • test_anova(*groups) – One-way ANOVA

  • +
  • test_anova_2way(data, factor1, factor2) – Two-way ANOVA

  • +
  • test_anova_rm(data, groups) – Repeated measures ANOVA

  • +
+

Non-parametric

+
    +
  • test_mannwhitneyu(g1, g2) – Mann-Whitney U test

  • +
  • test_wilcoxon(g1, g2) – Wilcoxon signed-rank test

  • +
  • test_kruskal(*groups) – Kruskal-Wallis H test

  • +
  • test_friedman(*groups) – Friedman test

  • +
  • test_brunner_munzel(g1, g2) – Brunner-Munzel test

  • +
+

Correlation

+
    +
  • test_pearson(x, y) – Pearson correlation

  • +
  • test_spearman(x, y) – Spearman rank correlation

  • +
  • test_kendall(x, y) – Kendall tau correlation

  • +
  • test_theilsen(x, y) – Theil-Sen robust regression

  • +
+

Categorical

+
    +
  • test_chi2(observed) – Chi-squared test

  • +
  • test_fisher(table) – Fisher’s exact test

  • +
  • test_mcnemar(table) – McNemar test

  • +
  • test_cochran_q(*groups) – Cochran’s Q test

  • +
+

Normality

+
    +
  • test_shapiro(data) – Shapiro-Wilk test

  • +
  • test_ks_1samp(data) – One-sample Kolmogorov-Smirnov test

  • +
  • test_ks_2samp(x, y) – Two-sample Kolmogorov-Smirnov test

  • +
  • test_normality(*samples) – Multi-sample normality check

  • +
+
+
+

Seaborn-Style Data Parameter

+

All two-sample and one-sample tests accept an optional data parameter +for DataFrame/CSV column resolution (like seaborn):

+
import pandas as pd
+
+df = pd.read_csv("experiment.csv")
+
+# Two-sample: column names as x/y
+result = stx.stats.test_ttest_ind(x="before", y="after", data=df)
+
+# One-sample: column name as x
+result = stx.stats.test_shapiro(x="scores", data=df)
+
+# Multi-group: value + group columns
+result = stx.stats.test_anova(data=df, value_col="score", group_col="treatment")
+
+# Also works with CSV path
+result = stx.stats.test_ttest_ind(x="col1", y="col2", data="data.csv")
+
+
+
+
+

Test Recommendation

+

Not sure which test to use? Let SciTeX recommend:

+
recommendations = stx.stats.recommend_tests(
+    n_groups=2,
+    sample_sizes=[30, 35],
+    outcome_type="continuous",
+    paired=False,
+)
+
+
+
+
+

Output Formats

+

Every test supports return_as parameter:

+
    +
  • "dict" (default) – Returns plain dict with all results

  • +
  • "dataframe" – Returns pandas DataFrame

  • +
+
+
+

Descriptive Statistics

+
stx.stats.describe(data)                    # mean, std, median, IQR, etc.
+stx.stats.effect_sizes.cohens_d(g1, g2)     # Cohen's d
+stx.stats.test_normality(g1, g2)            # Multi-sample normality
+stx.stats.p_to_stars(0.003)                 # "**"
+
+
+
+
+

Multiple Comparison Correction

+
corrected = stx.stats.correct_pvalues(
+    [0.01, 0.03, 0.05, 0.001],
+    method="fdr_bh",
+)
+
+
+
+
+

Post-hoc Tests

+
stx.stats.posthoc_test(
+    [g1, g2, g3],
+    group_names=["Control", "Treatment A", "Treatment B"],
+    method="tukey",
+)
+
+
+
+
+

API Reference

+

See scitex.stats API Reference for the auto-generated Python API.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/str.html b/src/scitex/_sphinx_html/modules/str.html new file mode 100644 index 000000000..dc2d1604f --- /dev/null +++ b/src/scitex/_sphinx_html/modules/str.html @@ -0,0 +1,681 @@ + + + + + + + + + str Module (stx.str) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

str Module (stx.str)

+

See scitex.str API Reference for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/template.html b/src/scitex/_sphinx_html/modules/template.html new file mode 100644 index 000000000..7f874c284 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/template.html @@ -0,0 +1,779 @@ + + + + + + + + + Template Module (stx.template) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Template Module (stx.template)

+

Project scaffolding and code snippet templates for quick starts.

+
+

Project Templates

+
# Clone a project template
+scitex template clone research my_project
+scitex template clone pip_project my_package
+scitex template clone paper my_paper
+
+
+ ++++ + + + + + + + + + + + + + + + + + + + + + + +

Template

Description

research

Full scientific workflow (scripts, data, docs, config)

research_minimal

Essential modules only

pip_project

Distributable Python package for PyPI

singularity

Reproducible containerized environment

paper_directory

Academic paper with LaTeX/BibTeX structure

+
+
+

Git Strategies

+ ++++ + + + + + + + + + + + + + + + + + + + +

Strategy

Behavior

child (default)

Fresh git repo, isolated from template

parent

Use parent repo if available

origin

Preserve template’s git history

None

No git initialization

+
+
+

Code Templates

+
# List available code templates
+scitex template code list
+
+# Get a template
+scitex template code session           # Full @stx.session script
+scitex template code session-minimal   # Lightweight session
+scitex template code session-plot      # Figure-focused session
+scitex template code session-stats     # Statistics-focused session
+scitex template code io                # I/O operations
+scitex template code plt               # Plotting patterns
+scitex template code stats             # Statistical analysis
+scitex template code scholar           # Literature management
+
+
+
+
+

Python API

+
from scitex.template import clone_template, get_code_template
+
+# Clone project
+clone_template("research", "my_experiment", git_strategy="child")
+
+# Get code snippet
+code = get_code_template("session", filepath="analyze.py")
+
+
+
+
+

API Reference

+

See scitex.template API Reference for the auto-generated Python API.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/tex.html b/src/scitex/_sphinx_html/modules/tex.html new file mode 100644 index 000000000..988e9c63a --- /dev/null +++ b/src/scitex/_sphinx_html/modules/tex.html @@ -0,0 +1,681 @@ + + + + + + + + + tex Module (stx.tex) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

tex Module (stx.tex)

+

See /api/scitex.tex for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/torch.html b/src/scitex/_sphinx_html/modules/torch.html new file mode 100644 index 000000000..2cccfe919 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/torch.html @@ -0,0 +1,681 @@ + + + + + + + + + torch Module (stx.torch) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

torch Module (stx.torch)

+

See /api/scitex.torch for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/tunnel.html b/src/scitex/_sphinx_html/modules/tunnel.html new file mode 100644 index 000000000..cbae132a2 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/tunnel.html @@ -0,0 +1,681 @@ + + + + + + + + + tunnel Module (stx.tunnel) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

tunnel Module (stx.tunnel)

+

See /api/scitex.tunnel for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/types.html b/src/scitex/_sphinx_html/modules/types.html new file mode 100644 index 000000000..ea14c604e --- /dev/null +++ b/src/scitex/_sphinx_html/modules/types.html @@ -0,0 +1,681 @@ + + + + + + + + + types Module (stx.types) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

types Module (stx.types)

+

See /api/scitex.types for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/ui.html b/src/scitex/_sphinx_html/modules/ui.html new file mode 100644 index 000000000..89c709623 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/ui.html @@ -0,0 +1,681 @@ + + + + + + + + + ui Module (stx.ui) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

ui Module (stx.ui)

+

See /api/scitex.ui for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/utils.html b/src/scitex/_sphinx_html/modules/utils.html new file mode 100644 index 000000000..a309c28ea --- /dev/null +++ b/src/scitex/_sphinx_html/modules/utils.html @@ -0,0 +1,681 @@ + + + + + + + + + utils Module (stx.utils) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

utils Module (stx.utils)

+

See /api/scitex.utils for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/web.html b/src/scitex/_sphinx_html/modules/web.html new file mode 100644 index 000000000..b2d023c7e --- /dev/null +++ b/src/scitex/_sphinx_html/modules/web.html @@ -0,0 +1,681 @@ + + + + + + + + + web Module (stx.web) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

web Module (stx.web)

+

See /api/scitex.web for the auto-generated Python API.

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/modules/writer.html b/src/scitex/_sphinx_html/modules/writer.html new file mode 100644 index 000000000..2563492b4 --- /dev/null +++ b/src/scitex/_sphinx_html/modules/writer.html @@ -0,0 +1,825 @@ + + + + + + + + + Writer Module (stx.writer) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Writer Module (stx.writer)

+

LaTeX manuscript compilation, figure/table management, bibliography +handling, and IMRAD writing guidelines.

+
+

Note

+

stx.writer wraps the standalone +scitex-writer package. +Install with: pip install scitex-writer.

+
+
+

Quick Reference

+
from scitex.writer import Writer
+from pathlib import Path
+
+writer = Writer(Path("my_paper"))
+
+# Compile manuscript → PDF
+result = writer.compile_manuscript()
+if result.success:
+    print(f"PDF: {result.output_pdf}")
+
+# Compile supplementary materials
+result = writer.compile_supplementary()
+
+# Compile revision with change tracking
+result = writer.compile_revision(track_changes=True)
+
+
+
+
+

CLI Usage

+
# Compile
+scitex writer compile manuscript ./my-paper
+scitex writer compile supplementary ./my-paper
+scitex writer compile revision ./my-paper --track-changes
+
+# Bibliography
+scitex writer bib list ./my-paper
+scitex writer bib add ./my-paper "@article{...}"
+
+# Tables and figures
+scitex writer tables add ./my-paper data.csv
+scitex writer figures list ./my-paper
+
+# Writing guidelines
+scitex writer guidelines get abstract
+
+
+
+
+

Project Structure

+

A writer project follows this layout:

+
my_paper/
++-- 00_shared/
+|   +-- bibliography.bib
+|   +-- figures/
+|   +-- tables/
++-- 01_manuscript/
+|   +-- main.tex
+|   +-- sections/
++-- 02_supplementary/
+|   +-- main.tex
++-- 03_revision/
+    +-- main.tex
+
+
+

Create a new project:

+
scitex template clone paper my_paper
+
+
+
+
+

Key Classes

+
    +
  • Writer – Main entry point for compilation and project management

  • +
  • CompilationResult – Compilation outcome (success, output path, logs)

  • +
  • ManuscriptTree – Document tree for the main manuscript

  • +
  • SupplementaryTree – Document tree for supplementary materials

  • +
  • RevisionTree – Document tree for revision responses

  • +
+
+
+

Document Management

+

Figures:

+
# Add figure (copies image + creates caption file)
+writer.add_figure("fig1", "plot.png", caption="Results")
+
+# List figures
+writer.list_figures()
+
+# Convert between formats
+writer.convert_figure("figure.pdf", "figure.png", dpi=300)
+
+
+

Tables:

+
# Add table from CSV
+writer.add_table("tab1", csv_content, caption="Demographics")
+
+# CSV ↔ LaTeX conversion
+writer.csv_to_latex("data.csv", "table.tex")
+writer.latex_to_csv("table.tex", "data.csv")
+
+
+

Bibliography:

+
# Add BibTeX entry
+writer.add_bibentry('@article{key, title={...}, ...}')
+
+# Merge multiple .bib files
+writer.merge_bibfiles(output_file="bibliography.bib")
+
+
+
+
+

Writing Guidelines

+

IMRAD guidelines for each manuscript section:

+
scitex writer guidelines list
+scitex writer guidelines get introduction
+
+
+
from scitex.writer import guidelines
+
+# Get guideline for a section
+guide = guidelines.get("methods")
+
+# Build editing prompt: guideline + draft
+prompt = guidelines.build("discussion", draft_text)
+
+
+
+
+

Compilation Options

+
result = writer.compile_manuscript(
+    timeout=300,        # Max compilation time (seconds)
+    no_figs=False,      # Exclude figures
+    no_tables=False,    # Exclude tables
+    draft=False,        # Draft mode (fast, lower quality)
+    dark_mode=False,    # Dark color scheme
+    quiet=False,        # Suppress output
+)
+
+
+
+
+

API Reference

+

See scitex.writer for the auto-generated Python API.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/objects.inv b/src/scitex/_sphinx_html/objects.inv new file mode 100644 index 000000000..227e21362 Binary files /dev/null and b/src/scitex/_sphinx_html/objects.inv differ diff --git a/src/scitex/_sphinx_html/py-modindex.html b/src/scitex/_sphinx_html/py-modindex.html new file mode 100644 index 000000000..c9c7ee16e --- /dev/null +++ b/src/scitex/_sphinx_html/py-modindex.html @@ -0,0 +1,1015 @@ + + + + + + + + Python Module Index — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ + +

Python Module Index

+ +
+ s +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+ s
+ scitex +
    + scitex.ai +
    + scitex.app +
    + scitex.audio +
    + scitex.audit +
    + scitex.benchmark +
    + scitex.bridge +
    + scitex.browser +
    + scitex.canvas +
    + scitex.capture +
    + scitex.clew +
    + scitex.cli +
    + scitex.cloud +
    + scitex.compat +
    + scitex.config +
    + scitex.container +
    + scitex.context +
    + scitex.cv +
    + scitex.dataset +
    + scitex.datetime +
    + scitex.db +
    + scitex.decorators +
    + scitex.dev +
    + scitex.diagram +
    + scitex.dict +
    + scitex.dsp +
    + scitex.etc +
    + scitex.events +
    + scitex.gen +
    + scitex.gists +
    + scitex.git +
    + scitex.introspect +
    + scitex.io +
    + scitex.linalg +
    + scitex.logging +
    + scitex.media +
    + scitex.module +
    + scitex.msword +
    + scitex.nn +
    + scitex.notebook +
    + scitex.notification +
    + scitex.os +
    + scitex.parallel +
    + scitex.path +
    + scitex.pd +
    + scitex.plt +
    + scitex.project +
    + scitex.repro +
    + scitex.resource +
    + scitex.rng +
    + scitex.schema +
    + scitex.scholar +
    + scitex.security +
    + scitex.session +
    + scitex.sh +
    + scitex.social +
    + scitex.stats +
    + scitex.str +
    + scitex.template +
    + scitex.tex +
    + scitex.torch +
    + scitex.tunnel +
    + scitex.types +
    + scitex.ui +
    + scitex.web +
    + scitex.writer +
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/quickstart.html b/src/scitex/_sphinx_html/quickstart.html new file mode 100644 index 000000000..d2948cf8e --- /dev/null +++ b/src/scitex/_sphinx_html/quickstart.html @@ -0,0 +1,907 @@ + + + + + + + + + Quick Start — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Quick Start

+
+

Three Interfaces

+

SciTeX exposes the same capabilities through three interfaces: +Python API, CLI commands, and MCP tools for AI agents.

+
+

Python API

+

@stx.session – Reproducible Experiment Tracking

+
import scitex as stx
+
+@stx.session
+def main(filename="demo.jpg"):
+    fig, ax = stx.plt.subplots()
+    ax.plot_line(t, signal)
+    ax.set_xyt("Time (s)", "Amplitude", "Title")
+    stx.io.save(fig, filename)
+    return 0
+
+
+

Output:

+
script_out/FINISHED_SUCCESS/2025-01-08_12-30-00_AbC1/
+|-- demo.jpg                    # Figure with embedded metadata
+|-- demo.csv                    # Auto-exported plot data
+|-- CONFIGS/CONFIG.yaml         # Reproducible parameters
++-- logs/{stdout,stderr}.log    # Execution logs
+
+
+

stx.io – Universal File I/O (30+ formats)

+
stx.io.save(df, "output.csv")
+stx.io.save(fig, "output.jpg")
+df = stx.io.load("output.csv")
+
+
+

stx.stats – Publication-Ready Statistics (23 tests)

+
result = stx.stats.test_ttest_ind(group1, group2, return_as="dataframe")
+# Includes: p-value, effect size, CI, normality check, power
+
+
+

stx.plt – Publication-Ready Figures

+
fig, ax = stx.plt.subplots()
+ax.plot_line(x, y, label="signal")
+ax.set_xyt("Time (s)", "Amplitude", "Sine Wave")
+stx.io.save(fig, "plot.png")
+# Creates: plot.png + plot.csv + plot.yaml (recipe for reproduction)
+
+
+

stx.scholar – Literature Management

+
# Enrich BibTeX with abstracts, DOIs, citation counts
+stx.scholar.enrich_bibtex("refs.bib", add_abstracts=True)
+
+
+
+
+

CLI Commands

+
scitex --help-recursive              # Show all commands
+scitex scholar fetch "10.1038/..."   # Download paper by DOI
+scitex scholar bibtex refs.bib       # Enrich BibTeX
+scitex stats recommend               # Suggest statistical tests
+scitex audio speak "Done"            # Text-to-speech
+scitex capture snap                  # Screenshot
+
+
+
+
+

MCP Tools (120+ tools for AI agents)

+

Turn AI agents into autonomous scientific researchers.

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Category

Tools

Description

writer

28

LaTeX manuscript compilation

scholar

23

PDF download, metadata enrichment

capture

12

Screen monitoring and capture

introspect

12

Python code introspection

audio

10

Text-to-speech, audio playback

stats

10

Automated statistical testing

plt

9

Matplotlib figure creation

diagram

9

Mermaid and Graphviz diagrams

dataset

8

Neuroimaging/physiology datasets

social

7

Social media posting

canvas

7

Figure composition

template

6

Project scaffolding

verify

6

Reproducibility verification

dev

6

Version checking

ui

5

Notifications

linter

3

Code pattern checking

+

Claude Code Setup — add .mcp.json to your project root. Use SCITEX_ENV_SRC +to load all configuration from a .src file — this keeps .mcp.json static +across environments:

+
{
+  "mcpServers": {
+    "scitex": {
+      "command": "scitex",
+      "args": ["mcp", "start"],
+      "env": {
+        "SCITEX_ENV_SRC": "${SCITEX_ENV_SRC}"
+      }
+    }
+  }
+}
+
+
+

Switch environments via your shell profile:

+
# Local machine
+export SCITEX_ENV_SRC=~/.scitex/scitex/local.src
+
+# Remote server
+export SCITEX_ENV_SRC=~/.scitex/scitex/remote.src
+
+
+

Generate a template .src file:

+
scitex env-template -o ~/.scitex/scitex/local.src
+
+
+

Or install globally:

+
scitex mcp install
+
+
+
+
+
+

Complete Example

+

A typical research script combining session, I/O, plotting, and statistics:

+
import scitex as stx
+
+@stx.session
+def main(
+    n=200,
+    noise=0.1,
+    CONFIG=stx.INJECTED,
+    plt=stx.INJECTED,
+    logger=stx.INJECTED,
+):
+    """Analyze noisy sine wave."""
+    import numpy as np
+
+    # Generate data
+    x = np.linspace(0, 4 * np.pi, n)
+    y_clean = np.sin(x)
+    y_noisy = y_clean + np.random.normal(0, noise, n)
+
+    # Save raw data
+    stx.io.save({"x": x, "y_clean": y_clean, "y_noisy": y_noisy}, "data.npz")
+
+    # Visualize
+    fig, ax = plt.subplots()
+    ax.plot_scatter(x, y_noisy, alpha=0.3, label="Noisy")
+    ax.plot_line(x, y_clean, color="red", label="True")
+    ax.set_xyt("x", "y", f"Sine Wave (noise={noise})")
+    stx.io.save(fig, "sine.png")
+
+    # Correlation between noisy and clean
+    result = stx.stats.test_pearson(y_clean, y_noisy)
+    logger.info(f"Pearson r={result['effect_size']:.3f}, p={result['pvalue']:.1e}")
+
+    # Save summary
+    stx.io.save({"r": result["effect_size"], "p": result["pvalue"]}, "stats.yaml")
+
+    return 0
+
+
+
+
+

Next Steps

+ +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/search.html b/src/scitex/_sphinx_html/search.html new file mode 100644 index 000000000..4786d7652 --- /dev/null +++ b/src/scitex/_sphinx_html/search.html @@ -0,0 +1,690 @@ + + + + + + + + Search — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ + + + +
+ +
+ +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/searchindex.js b/src/scitex/_sphinx_html/searchindex.js new file mode 100644 index 000000000..5a6437809 --- /dev/null +++ b/src/scitex/_sphinx_html/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({"alltitles":{"AI Module (stx.ai)":[[30,null],[99,null]],"API Reference":[[0,null],[30,"api-reference"],[39,"api-reference"],[43,"api-reference"],[50,"api-reference"],[52,"api-reference"],[54,"api-reference"],[57,"api-reference"],[61,"api-reference"],[62,"api-reference"],[64,"api-reference"],[65,"api-reference"],[69,"api-reference"],[75,"api-reference"],[76,"api-reference"],[78,"api-reference"],[82,"api-reference"],[84,"api-reference"],[87,"api-reference"],[89,"api-reference"],[97,"api-reference"],[99,"module-scitex.ai"],[100,"module-scitex.app"],[101,"module-scitex.audio"],[107,"module-scitex.capture"],[108,"module-scitex.clew"],[110,"module-scitex.cloud"],[112,"module-scitex.config"],[116,"module-scitex.dataset"],[119,"module-scitex.decorators"],[120,"module-scitex.dev"],[121,"module-scitex.diagram"],[123,"module-scitex.dsp"],[126,"module-scitex.gen"],[129,null],[130,"module-scitex.introspect"],[131,"module-scitex.io"],[133,"api-reference"],[134,"module-scitex.logging"],[138,"module-scitex.nn"],[140,"module-scitex.notification"],[144,"module-scitex.pd"],[147,"module-scitex.repro"],[151,"module-scitex.scholar"],[153,"module-scitex.session"],[156,"module-scitex.stats"],[158,"module-scitex.template"],[166,"module-scitex.writer"]],"API Tree":[[61,"api-tree"],[130,"api-tree"]],"App Module (stx.app)":[[100,null]],"Architecture":[[26,"architecture"]],"Architectures":[[69,"architectures"],[138,"architectures"]],"Area Plots":[[27,"area-plots"],[170,"area-plots"]],"Attention & Shape":[[69,"attention-shape"],[138,"attention-shape"]],"Audio":[[167,"audio"]],"Audio Module (stx.audio)":[[101,null]],"Auto-Ordering":[[50,"auto-ordering"],[119,"auto-ordering"]],"Available Functions":[[54,"available-functions"],[75,"available-functions"],[78,"available-functions"],[123,"available-functions"],[144,"available-functions"],[147,"available-functions"]],"Available Tests":[[87,"available-tests"],[156,"available-tests"]],"Available profiles:":[[137,"available-profiles"]],"Axis Helpers":[[76,"axis-helpers"]],"Axis Methods":[[145,"axis-methods"]],"Axis Utilities":[[145,"axis-utilities"]],"Backends":[[52,"backends"],[121,"backends"]],"Bar Plots":[[145,"bar-plots"]],"Basic Usage":[[84,"basic-usage"],[153,"basic-usage"]],"Batch Processing":[[50,"batch-processing"],[119,"batch-processing"]],"Best Practices":[[26,"best-practices"],[84,"best-practices"],[153,"best-practices"]],"Bulk Rename":[[120,"bulk-rename"]],"CLI Access":[[110,"cli-access"],[116,"cli-access"],[140,"cli-access"]],"CLI Commands":[[39,"cli-commands"],[98,"cli-commands"],[108,"cli-commands"]],"CLI Reference":[[167,null]],"CLI Usage":[[82,"cli-usage"],[97,"cli-usage"],[151,"cli-usage"],[166,"cli-usage"],[174,"cli-usage"]],"CONFIG Object":[[84,"config-object"],[153,"config-object"]],"Caching":[[50,"caching"],[119,"caching"],[131,"caching"]],"Capture":[[167,"capture"]],"Capture Module (stx.capture)":[[107,null]],"Categorical Plots":[[27,"categorical-plots"],[145,"categorical-plots"],[170,"categorical-plots"]],"Channel Manipulation":[[69,"channel-manipulation"],[138,"channel-manipulation"]],"Classification":[[30,"classification"],[99,"classification"]],"Clew Module (stx.clew)":[[39,null],[108,null]],"Cloud Module (stx.cloud)":[[110,null]],"Code Analysis":[[61,"code-analysis"],[130,"code-analysis"]],"Code Templates":[[89,"code-templates"],[158,"code-templates"]],"Compilation Options":[[97,"compilation-options"],[166,"compilation-options"]],"Complete Example":[[98,"complete-example"]],"Config Module (stx.config)":[[43,null],[112,null]],"Configuration":[[26,"configuration"],[64,"configuration"],[133,"configuration"],[168,"configuration"]],"Contour and Vector Plots":[[27,"contour-and-vector-plots"],[170,"contour-and-vector-plots"]],"Core":[[60,null],[129,null]],"Core Concepts":[[26,null],[168,null]],"Core Install":[[29,"core-install"],[172,"core-install"]],"Creating an Application":[[100,"creating-an-application"]],"DSP Module (stx.dsp)":[[54,null],[123,null]],"Data & I/O":[[60,null],[129,null]],"Data API":[[110,"data-api"]],"Data Sources":[[82,"data-sources"],[151,"data-sources"]],"Dataset Module (stx.dataset)":[[116,null]],"Decorator Options":[[84,"decorator-options"],[153,"decorator-options"]],"Decorators Module (stx.decorators)":[[50,null],[119,null]],"Delegation Pattern":[[168,"delegation-pattern"],[169,"delegation-pattern"]],"Dependency Chains":[[39,"dependency-chains"],[108,"dependency-chains"]],"Descriptive Statistics":[[87,"descriptive-statistics"],[156,"descriptive-statistics"]],"Dev":[[167,"dev"]],"Dev Module (stx.dev)":[[120,null]],"Development":[[129,null]],"Development Install":[[29,"development-install"],[172,"development-install"]],"Diagram Module (stx.diagram)":[[52,null],[121,null]],"Diagram Types":[[52,"diagram-types"],[121,"diagram-types"]],"Distribution Plots":[[27,"distribution-plots"],[145,"distribution-plots"],[170,"distribution-plots"]],"Document Management":[[97,"document-management"],[166,"document-management"]],"Documentation":[[61,"documentation"],[120,"documentation"],[130,"documentation"]],"Ecosystem":[[169,null]],"Ecosystem Management":[[120,"ecosystem-management"]],"Ecosystem Packages":[[120,"id1"]],"Example":[[13,null],[15,null],[15,null],[15,null],[15,null],[15,null],[114,null],[114,null],[115,null],[124,null],[124,null],[134,null],[143,null],[143,null],[143,null],[143,null],[143,null],[148,null],[148,null]],"Example Output":[[133,"example-output"]],"Example Workflow":[[173,"example-workflow"]],"Examples":[[2,null],[2,null],[2,null],[2,null],[3,null],[3,null],[3,null],[13,null],[13,null],[15,null],[18,null],[18,null],[18,null],[18,null],[18,null],[18,null],[18,null],[18,null],[18,null],[18,null],[18,null],[18,null],[25,null],[25,null],[25,null],[108,null],[108,null],[108,null],[108,null],[112,null],[112,null],[112,null],[114,null],[114,null],[114,null],[114,null],[120,null],[120,null],[120,null],[134,null],[134,null],[137,null],[137,null],[137,null],[137,null],[137,null],[137,null],[137,null],[137,null],[137,null],[137,null],[137,null],[137,null],[137,null],[137,null],[137,null],[139,null],[142,null],[143,null],[147,null],[147,null],[147,null],[147,null],[147,null],[147,null],[147,null],[147,null],[147,null],[147,null],[147,null],[147,null],[149,null],[149,null],[149,null],[149,null],[149,null],[149,null],[149,null],[149,null],[149,null],[149,null],[149,null],[149,null],[166,null],[166,null],[166,null]],"Exception Hierarchy":[[65,"exception-hierarchy"],[134,"exception-hierarchy"]],"Figure Composition":[[76,"figure-composition"]],"Figure Saving":[[62,"figure-saving"]],"Figure and Axis Creation":[[145,"figure-and-axis-creation"]],"File Storage":[[110,"file-storage"]],"Filtering and Sorting":[[82,"filtering-and-sorting"],[151,"filtering-and-sorting"]],"Format Registry":[[131,"format-registry"]],"Four Freedoms for Research":[[28,"four-freedoms-for-research"]],"Gallery":[[145,"gallery"]],"Gen Module (stx.gen)":[[57,null],[126,null]],"General":[[167,"general"]],"Getting Started":[[28,null],[171,"getting-started"],[171,null]],"Git Strategies":[[89,"git-strategies"],[158,"git-strategies"]],"Guides":[[171,null]],"HDF5 and Zarr Exploration":[[131,"hdf5-and-zarr-exploration"]],"HPC Testing":[[120,"hpc-testing"]],"Heatmaps":[[145,"heatmaps"]],"Heatmaps and Grids":[[27,"heatmaps-and-grids"],[170,"heatmaps-and-grids"]],"Hot Reload":[[120,"hot-reload"]],"How It Works":[[39,"how-it-works"],[84,"how-it-works"],[108,"how-it-works"],[153,"how-it-works"]],"I/O Module (stx.io)":[[131,null]],"IO Module (stx.io)":[[62,null]],"IPython-Style Shortcuts":[[61,"ipython-style-shortcuts"],[130,"ipython-style-shortcuts"]],"Implementations":[[100,"implementations"]],"Indices and tables":[[171,"indices-and-tables"]],"Infrastructure":[[60,null],[129,null]],"Injected Parameters":[[84,"injected-parameters"],[153,"injected-parameters"]],"Installation":[[29,null],[169,"installation"],[172,null]],"Integration with @stx.session":[[140,"integration-with-stx-session"]],"Interfaces":[[64,"interfaces"],[133,"interfaces"],[171,null]],"Introspect":[[167,"introspect"]],"Introspect Module (stx.introspect)":[[61,null],[130,null]],"Job Management":[[110,"job-management"]],"Key Classes":[[43,"key-classes"],[82,"key-classes"],[97,"key-classes"],[112,"key-classes"],[151,"key-classes"],[166,"key-classes"]],"Key Features":[[100,"key-features"],[110,"key-features"],[171,"key-features"]],"Key Functions":[[39,"key-functions"],[101,"key-functions"],[107,"key-functions"],[108,"key-functions"],[116,"key-functions"],[131,"key-functions"],[140,"key-functions"]],"LLM-friendly Types":[[120,"llm-friendly-types"]],"Line Plots":[[27,"line-plots"],[145,"line-plots"],[170,"line-plots"]],"Linter Module (stx.linter)":[[64,null],[133,null]],"Literature & Writing":[[60,null],[129,null]],"Literature Management":[[174,"literature-management"]],"Local Database":[[116,"local-database"]],"Log Levels":[[65,"log-levels"],[134,"log-levels"]],"Logging Module (stx.logging)":[[65,null],[134,null]],"MCP Interface":[[158,"mcp-interface"]],"MCP Server":[[173,null]],"MCP Tools (120+ tools for AI agents)":[[98,"mcp-tools-120-tools-for-ai-agents"]],"MCP and CLI Wrappers":[[120,"mcp-and-cli-wrappers"]],"MM-Based Layout":[[76,"mm-based-layout"]],"Machine Learning":[[60,null],[129,null]],"Metrics":[[30,"metrics"],[99,"metrics"]],"Modular Architecture":[[168,"modular-architecture"]],"Module Overview":[[60,null]],"Multiple Comparison Correction":[[87,"multiple-comparison-correction"],[156,"multiple-comparison-correction"]],"NN Module (stx.nn)":[[69,null],[138,null]],"Next Steps":[[98,"next-steps"]],"Node Emphasis":[[52,"node-emphasis"],[121,"node-emphasis"]],"Node Shapes":[[52,"node-shapes"],[121,"node-shapes"]],"Notes":[[137,null]],"Notification Module (stx.notification)":[[140,null]],"Other":[[30,"other"],[60,null],[99,"other"],[129,null]],"Other Functions":[[62,"other-functions"]],"Output Directory Structure":[[84,"output-directory-structure"],[153,"output-directory-structure"]],"Output Formats":[[87,"output-formats"],[156,"output-formats"]],"Overview":[[100,"overview"],[110,"overview"],[120,"overview"]],"PD Module (stx.pd)":[[75,null],[144,null]],"PLT Module (stx.plt)":[[76,null]],"Package Reference":[[169,"package-reference"]],"Paper Metadata":[[82,"paper-metadata"],[151,"paper-metadata"]],"Paper Modes":[[52,"paper-modes"],[121,"paper-modes"]],"Path Management":[[43,"path-management"],[112,"path-management"]],"Per-Module Extras":[[29,"per-module-extras"],[172,"per-module-extras"]],"Plot Gallery":[[27,null],[170,null]],"Plot Types (47)":[[76,"plot-types-47"]],"Plugin Architecture":[[133,"plugin-architecture"]],"Post-hoc Tests":[[87,"post-hoc-tests"],[156,"post-hoc-tests"]],"Priority Resolution":[[43,"priority-resolution"],[112,"priority-resolution"]],"Project Organization":[[82,"project-organization"],[151,"project-organization"]],"Project Structure":[[97,"project-structure"],[166,"project-structure"]],"Project Templates":[[89,"project-templates"],[158,"project-templates"]],"Provenance Tracking":[[62,"provenance-tracking"]],"Provenance Tracking (Clew)":[[26,"provenance-tracking-clew"]],"Publication-Ready Figures":[[174,"publication-ready-figures"]],"Python API":[[89,"python-api"],[98,"python-api"],[158,"python-api"]],"Quick Reference":[[30,"quick-reference"],[43,"quick-reference"],[50,"quick-reference"],[52,"quick-reference"],[54,"quick-reference"],[57,"quick-reference"],[61,"quick-reference"],[62,"quick-reference"],[64,"quick-reference"],[65,"quick-reference"],[69,"quick-reference"],[75,"quick-reference"],[76,"quick-reference"],[78,"quick-reference"],[82,"quick-reference"],[87,"quick-reference"],[97,"quick-reference"],[99,"quick-reference"],[112,"quick-reference"],[119,"quick-reference"],[121,"quick-reference"],[123,"quick-reference"],[126,"quick-reference"],[130,"quick-reference"],[134,"quick-reference"],[138,"quick-reference"],[144,"quick-reference"],[147,"quick-reference"],[151,"quick-reference"],[156,"quick-reference"],[166,"quick-reference"]],"Quick Start":[[98,null],[100,"quick-start"],[101,"quick-start"],[107,"quick-start"],[110,"quick-start"],[116,"quick-start"],[131,"quick-start"],[133,"quick-start"],[140,"quick-start"]],"Quickstart":[[174,null]],"RandomStateManager":[[78,"randomstatemanager"],[147,"randomstatemanager"]],"Recommended: Install Everything":[[29,"recommended-install-everything"],[172,"recommended-install-everything"]],"Reference":[[171,null]],"Repository Operations":[[110,"repository-operations"]],"Repro Module (stx.repro)":[[78,null],[147,null]],"Reproducible Figures":[[76,"reproducible-figures"]],"Requirements":[[29,"requirements"],[172,"requirements"]],"Research Workflow":[[26,"research-workflow"],[29,"research-workflow"],[172,"research-workflow"]],"Role in SciTeX Ecosystem":[[28,"role-in-scitex-ecosystem"]],"Rule Categories":[[64,"rule-categories"],[133,"rule-categories"]],"Scatter Plots":[[27,"scatter-plots"],[145,"scatter-plots"],[170,"scatter-plots"]],"Scholar":[[167,"scholar"]],"Scholar Module (stx.scholar)":[[82,null],[151,null]],"SciTeX Documentation":[[171,null]],"SciTeX \u2013 Modular Python Toolkit for Researchers and AI Agents":[[28,null]],"Science & Analysis":[[60,null],[129,null]],"Seaborn Wrappers":[[145,"seaborn-wrappers"]],"Seaborn-Style Data Parameter":[[87,"seaborn-style-data-parameter"],[156,"seaborn-style-data-parameter"]],"Session Decorator":[[168,"session-decorator"],[174,"session-decorator"]],"Session Decorator (@stx.session)":[[84,null],[153,null]],"Setup":[[173,"setup"]],"Severity Levels":[[64,"severity-levels"],[133,"severity-levels"]],"Signal Processing Layers":[[69,"signal-processing-layers"],[138,"signal-processing-layers"]],"Special Plots":[[27,"special-plots"],[145,"special-plots"],[170,"special-plots"]],"Statistical Analysis":[[174,"statistical-analysis"]],"Statistical Annotations":[[76,"statistical-annotations"]],"Statistical Plots":[[27,"statistical-plots"],[145,"statistical-plots"],[170,"statistical-plots"]],"Stats":[[167,"stats"]],"Stats Module (stx.stats)":[[87,null],[156,null]],"Storage Architecture":[[82,"storage-architecture"],[151,"storage-architecture"]],"Strategy:":[[137,"strategy"]],"Stream Redirection":[[65,"stream-redirection"],[134,"stream-redirection"]],"Style Presets":[[76,"style-presets"]],"Supported Backends":[[140,"supported-backends"]],"Supported Formats":[[62,"supported-formats"],[131,"supported-formats"]],"Supported Sources":[[116,"supported-sources"]],"Template":[[167,"template"]],"Template Module (stx.template)":[[89,null],[158,null]],"Test Recommendation":[[87,"test-recommendation"],[156,"test-recommendation"]],"The Session Model":[[26,"the-session-model"]],"Three Interfaces":[[98,"three-interfaces"],[168,"three-interfaces"]],"Tool Categories":[[173,"tool-categories"]],"Training":[[30,"training"],[99,"training"]],"Type Analysis":[[61,"type-analysis"],[130,"type-analysis"]],"Type Conversion":[[50,"type-conversion"],[119,"type-conversion"]],"Typical usage:":[[137,"typical-usage"]],"Unified File I/O":[[174,"unified-file-i-o"]],"Unified I/O":[[168,"unified-i-o"]],"Universal I/O":[[26,"universal-i-o"]],"Use Cases":[[101,"use-cases"],[107,"use-cases"]],"Using uv (recommended)":[[29,"using-uv-recommended"],[172,"using-uv-recommended"]],"Utilities":[[50,"utilities"],[60,null],[119,"utilities"],[129,null]],"Utility Functions":[[43,"utility-functions"],[112,"utility-functions"]],"Verification Levels":[[39,"verification-levels"],[108,"verification-levels"]],"Verification Statuses":[[39,"verification-statuses"],[108,"verification-statuses"]],"Verifying the Installation":[[29,"verifying-the-installation"],[172,"verifying-the-installation"]],"Visualization":[[30,"visualization"],[99,"visualization"]],"Warning Categories":[[65,"warning-categories"],[134,"warning-categories"]],"What You Get":[[158,"what-you-get"]],"What is MCP?":[[173,"what-is-mcp"]],"Writer Module (stx.writer)":[[97,null],[166,null]],"Writing Guidelines":[[97,"writing-guidelines"],[166,"writing-guidelines"]],"YAML Configuration":[[43,"yaml-configuration"],[112,"yaml-configuration"]],"YAML Specification":[[52,"yaml-specification"],[121,"yaml-specification"]],"app Module (stx.app)":[[31,null]],"audio Module (stx.audio)":[[32,null]],"audit Module (stx.audit)":[[33,null],[102,null]],"benchmark Module (stx.benchmark)":[[34,null],[103,null]],"bridge Module (stx.bridge)":[[35,null],[104,null]],"browser Module (stx.browser)":[[36,null],[105,null]],"canvas Module (stx.canvas)":[[37,null],[106,null]],"capture Module (stx.capture)":[[38,null]],"cli Module (stx.cli)":[[40,null],[109,null]],"cloud Module (stx.cloud)":[[41,null]],"compat Module (stx.compat)":[[42,null],[111,null]],"config(**kwargs)":[[140,"config-kwargs"]],"container Module (stx.container)":[[44,null],[113,null]],"context Module (stx.context)":[[45,null],[114,null]],"crop(image, region)":[[107,"crop-image-region"]],"cv Module (stx.cv)":[[46,null],[115,null]],"dataset Module (stx.dataset)":[[47,null]],"datetime Module (stx.datetime)":[[48,null],[117,null]],"db Module (stx.db)":[[49,null],[118,null]],"dev Module (stx.dev)":[[51,null]],"dict Module (stx.dict)":[[53,null],[122,null]],"etc Module (stx.etc)":[[55,null],[124,null]],"events Module (stx.events)":[[56,null],[125,null]],"fetch(source, **kwargs)":[[116,"fetch-source-kwargs"]],"gists Module (stx.gists)":[[58,null],[127,null]],"git Module (stx.git)":[[59,null],[128,null]],"linalg Module (stx.linalg)":[[63,null],[132,null]],"list_backends()":[[101,"list-backends"],[140,"list-backends"]],"list_formats()":[[131,"list-formats"]],"list_sources()":[[116,"list-sources"]],"load(path)":[[131,"load-path"]],"load_configs(pattern=\"./config/*.yaml\")":[[131,"load-configs-pattern-config-yaml"]],"media Module (stx.media)":[[66,null],[135,null]],"module Module (stx.module)":[[67,null],[136,null]],"monitor(interval=1.0, duration=None, output_dir=\".\")":[[107,"monitor-interval-1-0-duration-none-output-dir"]],"msword Module (stx.msword)":[[68,null],[137,null]],"notebook Module (stx.notebook)":[[70,null],[139,null]],"notify Module (stx.notify)":[[71,null]],"os Module (stx.os)":[[72,null],[141,null]],"parallel Module (stx.parallel)":[[73,null],[142,null]],"path Module (stx.path)":[[74,null],[143,null]],"play(path)":[[101,"play-path"]],"project Module (stx.project)":[[77,null],[146,null]],"resource Module (stx.resource)":[[79,null],[148,null]],"rng Module (stx.rng)":[[80,null],[149,null]],"save(obj, path, **kwargs)":[[131,"save-obj-path-kwargs"]],"schema Module (stx.schema)":[[81,null],[150,null]],"scitex.ai API Reference":[[1,null]],"scitex.clew API Reference":[[2,null]],"scitex.config API Reference":[[3,null]],"scitex.db API Reference":[[4,null]],"scitex.decorators API Reference":[[5,null]],"scitex.diagram API Reference":[[6,null]],"scitex.dict API Reference":[[7,null]],"scitex.dsp API Reference":[[8,null]],"scitex.gen API Reference":[[9,null]],"scitex.introspect API Reference":[[10,null]],"scitex.io API Reference":[[11,null]],"scitex.linter":[[12,null]],"scitex.logging API Reference":[[13,null]],"scitex.nn API Reference":[[14,null]],"scitex.path API Reference":[[15,null]],"scitex.pd API Reference":[[16,null]],"scitex.plt":[[145,null]],"scitex.plt API Reference":[[17,null]],"scitex.repro API Reference":[[18,null]],"scitex.scholar API Reference":[[19,null]],"scitex.session API Reference":[[20,null]],"scitex.social API Reference":[[21,null]],"scitex.stats API Reference":[[22,null]],"scitex.str API Reference":[[23,null]],"scitex.template API Reference":[[24,null]],"scitex.writer":[[25,null]],"search(query, source=None)":[[116,"search-query-source-none"]],"security Module (stx.security)":[[83,null],[152,null]],"send(message, backend=None, **kwargs)":[[140,"send-message-backend-none-kwargs"]],"sh Module (stx.sh)":[[85,null],[154,null]],"snap(region=None)":[[107,"snap-region-none"]],"social Module (stx.social)":[[86,null],[155,null]],"speak(text, backend=None, **kwargs)":[[101,"speak-text-backend-none-kwargs"]],"str Module (stx.str)":[[88,null],[157,null]],"tex Module (stx.tex)":[[90,null],[159,null]],"torch Module (stx.torch)":[[91,null],[160,null]],"tunnel Module (stx.tunnel)":[[92,null],[161,null]],"types Module (stx.types)":[[93,null],[162,null]],"ui Module (stx.ui)":[[94,null],[163,null]],"utils Module (stx.utils)":[[95,null],[164,null]],"web Module (stx.web)":[[96,null],[165,null]]},"docnames":["api/index","api/scitex.ai","api/scitex.clew","api/scitex.config","api/scitex.db","api/scitex.decorators","api/scitex.diagram","api/scitex.dict","api/scitex.dsp","api/scitex.gen","api/scitex.introspect","api/scitex.io","api/scitex.linter","api/scitex.logging","api/scitex.nn","api/scitex.path","api/scitex.pd","api/scitex.plt","api/scitex.repro","api/scitex.scholar","api/scitex.session","api/scitex.social","api/scitex.stats","api/scitex.str","api/scitex.template","api/scitex.writer","core_concepts","gallery","index","installation","modules/ai","modules/app","modules/audio","modules/audit","modules/benchmark","modules/bridge","modules/browser","modules/canvas","modules/capture","modules/clew","modules/cli","modules/cloud","modules/compat","modules/config","modules/container","modules/context","modules/cv","modules/dataset","modules/datetime","modules/db","modules/decorators","modules/dev","modules/diagram","modules/dict","modules/dsp","modules/etc","modules/events","modules/gen","modules/gists","modules/git","modules/index","modules/introspect","modules/io","modules/linalg","modules/linter","modules/logging","modules/media","modules/module","modules/msword","modules/nn","modules/notebook","modules/notify","modules/os","modules/parallel","modules/path","modules/pd","modules/plt","modules/project","modules/repro","modules/resource","modules/rng","modules/schema","modules/scholar","modules/security","modules/session","modules/sh","modules/social","modules/stats","modules/str","modules/template","modules/tex","modules/torch","modules/tunnel","modules/types","modules/ui","modules/utils","modules/web","modules/writer","quickstart","source/api/ai","source/api/app","source/api/audio","source/api/audit","source/api/benchmark","source/api/bridge","source/api/browser","source/api/canvas","source/api/capture","source/api/clew","source/api/cli","source/api/cloud","source/api/compat","source/api/config","source/api/container","source/api/context","source/api/cv","source/api/dataset","source/api/datetime","source/api/db","source/api/decorators","source/api/dev","source/api/diagram","source/api/dict","source/api/dsp","source/api/etc","source/api/events","source/api/gen","source/api/gists","source/api/git","source/api/index","source/api/introspect","source/api/io","source/api/linalg","source/api/linter","source/api/logging","source/api/media","source/api/module","source/api/msword","source/api/nn","source/api/notebook","source/api/notification","source/api/os","source/api/parallel","source/api/path","source/api/pd","source/api/plt","source/api/project","source/api/repro","source/api/resource","source/api/rng","source/api/schema","source/api/scholar","source/api/security","source/api/session","source/api/sh","source/api/social","source/api/stats","source/api/str","source/api/template","source/api/tex","source/api/torch","source/api/tunnel","source/api/types","source/api/ui","source/api/utils","source/api/web","source/api/writer","source/cli","source/concepts","source/ecosystem","source/gallery","source/index","source/installation","source/mcp","source/quickstart"],"envversion":{"nbsphinx":4,"sphinx":66,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2,"sphinx.ext.intersphinx":1,"sphinx.ext.viewcode":1},"filenames":["api/index.rst","api/scitex.ai.rst","api/scitex.clew.rst","api/scitex.config.rst","api/scitex.db.rst","api/scitex.decorators.rst","api/scitex.diagram.rst","api/scitex.dict.rst","api/scitex.dsp.rst","api/scitex.gen.rst","api/scitex.introspect.rst","api/scitex.io.rst","api/scitex.linter.rst","api/scitex.logging.rst","api/scitex.nn.rst","api/scitex.path.rst","api/scitex.pd.rst","api/scitex.plt.rst","api/scitex.repro.rst","api/scitex.scholar.rst","api/scitex.session.rst","api/scitex.social.rst","api/scitex.stats.rst","api/scitex.str.rst","api/scitex.template.rst","api/scitex.writer.rst","core_concepts.rst","gallery.rst","index.rst","installation.rst","modules/ai.rst","modules/app.rst","modules/audio.rst","modules/audit.rst","modules/benchmark.rst","modules/bridge.rst","modules/browser.rst","modules/canvas.rst","modules/capture.rst","modules/clew.rst","modules/cli.rst","modules/cloud.rst","modules/compat.rst","modules/config.rst","modules/container.rst","modules/context.rst","modules/cv.rst","modules/dataset.rst","modules/datetime.rst","modules/db.rst","modules/decorators.rst","modules/dev.rst","modules/diagram.rst","modules/dict.rst","modules/dsp.rst","modules/etc.rst","modules/events.rst","modules/gen.rst","modules/gists.rst","modules/git.rst","modules/index.rst","modules/introspect.rst","modules/io.rst","modules/linalg.rst","modules/linter.rst","modules/logging.rst","modules/media.rst","modules/module.rst","modules/msword.rst","modules/nn.rst","modules/notebook.rst","modules/notify.rst","modules/os.rst","modules/parallel.rst","modules/path.rst","modules/pd.rst","modules/plt.rst","modules/project.rst","modules/repro.rst","modules/resource.rst","modules/rng.rst","modules/schema.rst","modules/scholar.rst","modules/security.rst","modules/session.rst","modules/sh.rst","modules/social.rst","modules/stats.rst","modules/str.rst","modules/template.rst","modules/tex.rst","modules/torch.rst","modules/tunnel.rst","modules/types.rst","modules/ui.rst","modules/utils.rst","modules/web.rst","modules/writer.rst","quickstart.rst","source/api/ai.rst","source/api/app.rst","source/api/audio.rst","source/api/audit.rst","source/api/benchmark.rst","source/api/bridge.rst","source/api/browser.rst","source/api/canvas.rst","source/api/capture.rst","source/api/clew.rst","source/api/cli.rst","source/api/cloud.rst","source/api/compat.rst","source/api/config.rst","source/api/container.rst","source/api/context.rst","source/api/cv.rst","source/api/dataset.rst","source/api/datetime.rst","source/api/db.rst","source/api/decorators.rst","source/api/dev.rst","source/api/diagram.rst","source/api/dict.rst","source/api/dsp.rst","source/api/etc.rst","source/api/events.rst","source/api/gen.rst","source/api/gists.rst","source/api/git.rst","source/api/index.rst","source/api/introspect.rst","source/api/io.rst","source/api/linalg.rst","source/api/linter.rst","source/api/logging.rst","source/api/media.rst","source/api/module.rst","source/api/msword.rst","source/api/nn.rst","source/api/notebook.rst","source/api/notification.rst","source/api/os.rst","source/api/parallel.rst","source/api/path.rst","source/api/pd.rst","source/api/plt.rst","source/api/project.rst","source/api/repro.rst","source/api/resource.rst","source/api/rng.rst","source/api/schema.rst","source/api/scholar.rst","source/api/security.rst","source/api/session.rst","source/api/sh.rst","source/api/social.rst","source/api/stats.rst","source/api/str.rst","source/api/template.rst","source/api/tex.rst","source/api/torch.rst","source/api/tunnel.rst","source/api/types.rst","source/api/ui.rst","source/api/utils.rst","source/api/web.rst","source/api/writer.rst","source/cli.rst","source/concepts.rst","source/ecosystem.rst","source/gallery.rst","source/index.rst","source/installation.rst","source/mcp.rst","source/quickstart.rst"],"indexentries":{"__call__() (scitex.repro.randomstatemanager method)":[[18,"scitex.repro.RandomStateManager.__call__",false],[147,"scitex.repro.RandomStateManager.__call__",false]],"__call__() (scitex.rng.randomstatemanager method)":[[149,"scitex.rng.RandomStateManager.__call__",false]],"__init__() (scitex.config.priorityconfig method)":[[3,"scitex.config.PriorityConfig.__init__",false],[112,"scitex.config.PriorityConfig.__init__",false]],"__init__() (scitex.config.scitexconfig method)":[[3,"scitex.config.ScitexConfig.__init__",false],[112,"scitex.config.ScitexConfig.__init__",false]],"__init__() (scitex.config.scitexpaths method)":[[3,"scitex.config.ScitexPaths.__init__",false],[112,"scitex.config.ScitexPaths.__init__",false]],"__init__() (scitex.logging.scitexerror method)":[[13,"scitex.logging.SciTeXError.__init__",false],[134,"scitex.logging.SciTeXError.__init__",false]],"__init__() (scitex.msword.wordreader method)":[[137,"scitex.msword.WordReader.__init__",false]],"__init__() (scitex.msword.wordwriter method)":[[137,"scitex.msword.WordWriter.__init__",false]],"__init__() (scitex.repro.randomstatemanager method)":[[18,"scitex.repro.RandomStateManager.__init__",false],[147,"scitex.repro.RandomStateManager.__init__",false]],"__init__() (scitex.rng.randomstatemanager method)":[[149,"scitex.rng.RandomStateManager.__init__",false]],"__init__() (scitex.writer.writer method)":[[25,"scitex.writer.Writer.__init__",false],[166,"scitex.writer.Writer.__init__",false]],"__post_init__() (scitex.dev.result method)":[[120,"scitex.dev.Result.__post_init__",false]],"__post_init__() (scitex.writer.manuscripttree method)":[[25,"scitex.writer.ManuscriptTree.__post_init__",false],[166,"scitex.writer.ManuscriptTree.__post_init__",false]],"__post_init__() (scitex.writer.revisiontree method)":[[25,"scitex.writer.RevisionTree.__post_init__",false],[166,"scitex.writer.RevisionTree.__post_init__",false]],"__post_init__() (scitex.writer.supplementarytree method)":[[25,"scitex.writer.SupplementaryTree.__post_init__",false],[166,"scitex.writer.SupplementaryTree.__post_init__",false]],"__str__() (scitex.writer.compilationresult method)":[[25,"scitex.writer.CompilationResult.__str__",false],[166,"scitex.writer.CompilationResult.__str__",false]],"accept_all_tracked_changes() (in module scitex.msword)":[[137,"scitex.msword.accept_all_tracked_changes",false]],"add_claim() (in module scitex.clew)":[[2,"scitex.clew.add_claim",false],[108,"scitex.clew.add_claim",false]],"add_dry_run_argument() (in module scitex.dev)":[[120,"scitex.dev.add_dry_run_argument",false]],"add_json_argument() (in module scitex.dev)":[[120,"scitex.dev.add_json_argument",false]],"apply_comments_as_edits() (in module scitex.msword)":[[137,"scitex.msword.apply_comments_as_edits",false]],"archive (scitex.writer.manuscripttree attribute)":[[25,"scitex.writer.ManuscriptTree.archive",false],[166,"scitex.writer.ManuscriptTree.archive",false]],"archive (scitex.writer.revisiontree attribute)":[[25,"scitex.writer.RevisionTree.archive",false],[166,"scitex.writer.RevisionTree.archive",false]],"archive (scitex.writer.supplementarytree attribute)":[[25,"scitex.writer.SupplementaryTree.archive",false],[166,"scitex.writer.SupplementaryTree.archive",false]],"arrow() (in module scitex.cv)":[[115,"scitex.cv.arrow",false]],"async_wrap_as_mcp() (in module scitex.dev)":[[120,"scitex.dev.async_wrap_as_mcp",false]],"audit() (in module scitex.audit)":[[102,"scitex.audit.audit",false]],"authenticationerror":[[13,"scitex.logging.AuthenticationError",false],[134,"scitex.logging.AuthenticationError",false]],"axiserror":[[13,"scitex.logging.AxisError",false],[134,"scitex.logging.AxisError",false]],"base (scitex.config.scitexpaths property)":[[3,"scitex.config.ScitexPaths.base",false],[112,"scitex.config.ScitexPaths.base",false]],"base (scitex.writer.manuscripttree attribute)":[[25,"scitex.writer.ManuscriptTree.base",false],[166,"scitex.writer.ManuscriptTree.base",false]],"base (scitex.writer.revisiontree attribute)":[[25,"scitex.writer.RevisionTree.base",false],[166,"scitex.writer.RevisionTree.base",false]],"base (scitex.writer.supplementarytree attribute)":[[25,"scitex.writer.SupplementaryTree.base",false],[166,"scitex.writer.SupplementaryTree.base",false]],"basewordprofile (class in scitex.msword)":[[137,"scitex.msword.BaseWordProfile",false]],"bibtexenrichmenterror":[[13,"scitex.logging.BibTeXEnrichmentError",false],[134,"scitex.logging.BibTeXEnrichmentError",false]],"blur() (in module scitex.cv)":[[115,"scitex.cv.blur",false]],"body_font (scitex.msword.basewordprofile attribute)":[[137,"scitex.msword.BaseWordProfile.body_font",false]],"body_font_size_pt (scitex.msword.basewordprofile attribute)":[[137,"scitex.msword.BaseWordProfile.body_font_size_pt",false]],"browser (scitex.config.scitexpaths property)":[[3,"scitex.config.ScitexPaths.browser",false],[112,"scitex.config.ScitexPaths.browser",false]],"browser_persistent (scitex.config.scitexpaths property)":[[3,"scitex.config.ScitexPaths.browser_persistent",false],[112,"scitex.config.ScitexPaths.browser_persistent",false]],"browser_screenshots (scitex.config.scitexpaths property)":[[3,"scitex.config.ScitexPaths.browser_screenshots",false],[112,"scitex.config.ScitexPaths.browser_screenshots",false]],"browser_sessions (scitex.config.scitexpaths property)":[[3,"scitex.config.ScitexPaths.browser_sessions",false],[112,"scitex.config.ScitexPaths.browser_sessions",false]],"buffer (scitex.logging.tee property)":[[13,"scitex.logging.Tee.buffer",false],[134,"scitex.logging.Tee.buffer",false]],"build_docs() (in module scitex.dev)":[[120,"scitex.dev.build_docs",false]],"build_tree() (in module scitex.app)":[[100,"scitex.app.build_tree",false]],"cache (scitex.config.scitexpaths property)":[[3,"scitex.config.ScitexPaths.cache",false],[112,"scitex.config.ScitexPaths.cache",false]],"caption_style (scitex.msword.basewordprofile attribute)":[[137,"id3",false],[137,"scitex.msword.BaseWordProfile.caption_style",false]],"capture (scitex.config.scitexpaths property)":[[3,"scitex.config.ScitexPaths.capture",false],[112,"scitex.config.ScitexPaths.capture",false]],"chain() (in module scitex.clew)":[[2,"scitex.clew.chain",false],[108,"scitex.clew.chain",false]],"check_file_exists() (in module scitex.logging)":[[13,"scitex.logging.check_file_exists",false],[134,"scitex.logging.check_file_exists",false]],"check_github_alerts() (in module scitex.security)":[[152,"scitex.security.check_github_alerts",false]],"check_host() (in module scitex.os)":[[141,"scitex.os.check_host",false]],"check_notebook() (in module scitex.notebook)":[[139,"scitex.notebook.check_notebook",false]],"check_path() (in module scitex.logging)":[[13,"scitex.logging.check_path",false],[134,"scitex.logging.check_path",false]],"check_shape_compatibility() (in module scitex.logging)":[[13,"scitex.logging.check_shape_compatibility",false],[134,"scitex.logging.check_shape_compatibility",false]],"check_stamp() (in module scitex.clew)":[[2,"scitex.clew.check_stamp",false],[108,"scitex.clew.check_stamp",false]],"check_versions() (in module scitex.dev)":[[120,"scitex.dev.check_versions",false]],"checkpoint() (scitex.repro.randomstatemanager method)":[[18,"scitex.repro.RandomStateManager.checkpoint",false],[147,"scitex.repro.RandomStateManager.checkpoint",false]],"checkpoint() (scitex.rng.randomstatemanager method)":[[149,"scitex.rng.RandomStateManager.checkpoint",false]],"circle() (in module scitex.cv)":[[115,"scitex.cv.circle",false]],"classify_exception() (in module scitex.dev)":[[120,"scitex.dev.classify_exception",false]],"clean() (in module scitex.path)":[[15,"scitex.path.clean",false],[143,"scitex.path.clean",false]],"clear_cache() (scitex.repro.randomstatemanager method)":[[18,"scitex.repro.RandomStateManager.clear_cache",false],[147,"scitex.repro.RandomStateManager.clear_cache",false]],"clear_cache() (scitex.rng.randomstatemanager method)":[[149,"scitex.rng.RandomStateManager.clear_cache",false]],"clear_highlights() (in module scitex.msword)":[[137,"scitex.msword.clear_highlights",false]],"clear_log() (scitex.config.priorityconfig method)":[[3,"scitex.config.PriorityConfig.clear_log",false],[112,"scitex.config.PriorityConfig.clear_log",false]],"close() (scitex.logging.tee method)":[[13,"scitex.logging.Tee.close",false],[134,"scitex.logging.Tee.close",false]],"columns (scitex.msword.basewordprofile attribute)":[[137,"id10",false],[137,"scitex.msword.BaseWordProfile.columns",false]],"compilationresult (class in scitex.writer)":[[25,"scitex.writer.CompilationResult",false],[166,"scitex.writer.CompilationResult",false]],"compile_manuscript() (scitex.writer.writer method)":[[25,"scitex.writer.Writer.compile_manuscript",false],[166,"scitex.writer.Writer.compile_manuscript",false]],"compile_notebook() (in module scitex.notebook)":[[139,"scitex.notebook.compile_notebook",false]],"compile_revision() (scitex.writer.writer method)":[[25,"scitex.writer.Writer.compile_revision",false],[166,"scitex.writer.Writer.compile_revision",false]],"compile_supplementary() (scitex.writer.writer method)":[[25,"scitex.writer.Writer.compile_supplementary",false],[166,"scitex.writer.Writer.compile_supplementary",false]],"compilednotebook (class in scitex.notebook)":[[139,"scitex.notebook.CompiledNotebook",false]],"config_path (scitex.config.scitexconfig property)":[[3,"scitex.config.ScitexConfig.config_path",false],[112,"scitex.config.ScitexConfig.config_path",false]],"configfilenotfounderror":[[13,"scitex.logging.ConfigFileNotFoundError",false],[134,"scitex.logging.ConfigFileNotFoundError",false]],"configkeyerror":[[13,"scitex.logging.ConfigKeyError",false],[134,"scitex.logging.ConfigKeyError",false]],"configurationerror":[[13,"scitex.logging.ConfigurationError",false],[134,"scitex.logging.ConfigurationError",false]],"configure() (in module scitex.logging)":[[13,"scitex.logging.configure",false],[134,"scitex.logging.configure",false]],"contents (scitex.writer.manuscripttree attribute)":[[25,"scitex.writer.ManuscriptTree.contents",false],[166,"scitex.writer.ManuscriptTree.contents",false]],"contents (scitex.writer.revisiontree attribute)":[[25,"scitex.writer.RevisionTree.contents",false],[166,"scitex.writer.RevisionTree.contents",false]],"contents (scitex.writer.supplementarytree attribute)":[[25,"scitex.writer.SupplementaryTree.contents",false],[166,"scitex.writer.SupplementaryTree.contents",false]],"context (scitex.dev.result attribute)":[[120,"scitex.dev.Result.context",false]],"convert_docx_to_tex() (in module scitex.msword)":[[137,"scitex.msword.convert_docx_to_tex",false]],"convert_notebook() (in module scitex.notebook)":[[139,"scitex.notebook.convert_notebook",false]],"copy() (scitex.app.filesbackend method)":[[100,"scitex.app.FilesBackend.copy",false]],"copy_file() (in module scitex.app)":[[100,"scitex.app.copy_file",false]],"count() (in module scitex.etc)":[[124,"scitex.etc.count",false]],"count_grids() (in module scitex.etc)":[[124,"scitex.etc.count_grids",false]],"create_post_import_hook() (in module scitex.msword)":[[137,"scitex.msword.create_post_import_hook",false]],"create_relative_symlink() (in module scitex.path)":[[15,"scitex.path.create_relative_symlink",false],[143,"scitex.path.create_relative_symlink",false]],"crop() (in module scitex.cv)":[[115,"scitex.cv.crop",false]],"dag (scitex.notebook.compilednotebook attribute)":[[139,"id2",false],[139,"scitex.notebook.CompiledNotebook.dag",false]],"dag() (in module scitex.clew)":[[2,"scitex.clew.dag",false],[108,"scitex.clew.dag",false]],"data (scitex.dev.result attribute)":[[120,"scitex.dev.Result.data",false]],"dataerror":[[13,"scitex.logging.DataError",false],[134,"scitex.logging.DataError",false]],"datalosswarning":[[13,"scitex.logging.DataLossWarning",false],[134,"scitex.logging.DataLossWarning",false]],"delete() (scitex.app.filesbackend method)":[[100,"scitex.app.FilesBackend.delete",false]],"delete() (scitex.writer.writer method)":[[25,"scitex.writer.Writer.delete",false],[166,"scitex.writer.Writer.delete",false]],"delete_file() (in module scitex.app)":[[100,"scitex.app.delete_file",false]],"denoise() (in module scitex.cv)":[[115,"scitex.cv.denoise",false]],"deprecated() (in module scitex.compat)":[[111,"scitex.compat.deprecated",false]],"description (scitex.msword.basewordprofile attribute)":[[137,"id1",false],[137,"scitex.msword.BaseWordProfile.description",false]],"detect_environment() (in module scitex.context)":[[114,"scitex.context.detect_environment",false]],"diff_docx() (in module scitex.msword)":[[137,"scitex.msword.diff_docx",false]],"diff_pdf (scitex.writer.compilationresult attribute)":[[25,"scitex.writer.CompilationResult.diff_pdf",false],[166,"scitex.writer.CompilationResult.diff_pdf",false]],"docs (scitex.writer.revisiontree attribute)":[[25,"scitex.writer.RevisionTree.docs",false],[166,"scitex.writer.RevisionTree.docs",false]],"doiresolutionerror":[[13,"scitex.logging.DOIResolutionError",false],[134,"scitex.logging.DOIResolutionError",false]],"double_anonymous (scitex.msword.basewordprofile attribute)":[[137,"id11",false],[137,"scitex.msword.BaseWordProfile.double_anonymous",false]],"dry_run_option() (in module scitex.dev)":[[120,"scitex.dev.dry_run_option",false]],"dtypeerror":[[13,"scitex.logging.DTypeError",false],[134,"scitex.logging.DTypeError",false]],"duration (scitex.writer.compilationresult attribute)":[[25,"scitex.writer.CompilationResult.duration",false],[166,"scitex.writer.CompilationResult.duration",false]],"edge_detect() (in module scitex.cv)":[[115,"scitex.cv.edge_detect",false]],"enable_file_logging() (in module scitex.logging)":[[13,"scitex.logging.enable_file_logging",false],[134,"scitex.logging.enable_file_logging",false]],"enable_track_changes() (in module scitex.msword)":[[137,"scitex.msword.enable_track_changes",false]],"enrichmenterror":[[13,"scitex.logging.EnrichmentError",false],[134,"scitex.logging.EnrichmentError",false]],"ensure_all() (scitex.config.scitexpaths method)":[[3,"scitex.config.ScitexPaths.ensure_all",false],[112,"scitex.config.ScitexPaths.ensure_all",false]],"ensure_dir() (scitex.config.scitexpaths method)":[[3,"scitex.config.ScitexPaths.ensure_dir",false],[112,"scitex.config.ScitexPaths.ensure_dir",false]],"equation_style (scitex.msword.basewordprofile attribute)":[[137,"id9",false],[137,"scitex.msword.BaseWordProfile.equation_style",false]],"error (scitex.dev.result attribute)":[[120,"scitex.dev.Result.error",false]],"error_code (scitex.dev.result attribute)":[[120,"scitex.dev.Result.error_code",false]],"errorcode (class in scitex.dev)":[[120,"scitex.dev.ErrorCode",false]],"errors (scitex.writer.compilationresult attribute)":[[25,"scitex.writer.CompilationResult.errors",false],[166,"scitex.writer.CompilationResult.errors",false]],"execution_order (scitex.notebook.compilednotebook attribute)":[[139,"id1",false],[139,"scitex.notebook.CompiledNotebook.execution_order",false]],"exists() (scitex.app.filesbackend method)":[[100,"scitex.app.FilesBackend.exists",false]],"exit_code (scitex.dev.errorcode property)":[[120,"scitex.dev.ErrorCode.exit_code",false]],"exit_code (scitex.dev.result property)":[[120,"scitex.dev.Result.exit_code",false]],"exit_code (scitex.writer.compilationresult attribute)":[[25,"scitex.writer.CompilationResult.exit_code",false],[166,"scitex.writer.CompilationResult.exit_code",false]],"export_claims_json() (in module scitex.clew)":[[2,"scitex.clew.export_claims_json",false],[108,"scitex.clew.export_claims_json",false]],"extract_comments() (in module scitex.msword)":[[137,"scitex.msword.extract_comments",false]],"extract_highlights() (in module scitex.msword)":[[137,"scitex.msword.extract_highlights",false]],"extract_tracked_changes() (in module scitex.msword)":[[137,"scitex.msword.extract_tracked_changes",false]],"figure_caption_prefixes (scitex.msword.basewordprofile attribute)":[[137,"id6",false],[137,"scitex.msword.BaseWordProfile.figure_caption_prefixes",false]],"figurenotfounderror":[[13,"scitex.logging.FigureNotFoundError",false],[134,"scitex.logging.FigureNotFoundError",false]],"file_exists() (in module scitex.app)":[[100,"scitex.app.file_exists",false]],"fileformaterror":[[13,"scitex.logging.FileFormatError",false],[134,"scitex.logging.FileFormatError",false]],"fileno() (scitex.logging.tee method)":[[13,"scitex.logging.Tee.fileno",false],[134,"scitex.logging.Tee.fileno",false]],"filesbackend (class in scitex.app)":[[100,"scitex.app.FilesBackend",false]],"filterwarnings() (in module scitex.logging)":[[13,"scitex.logging.filterwarnings",false],[134,"scitex.logging.filterwarnings",false]],"find_dir() (in module scitex.path)":[[15,"scitex.path.find_dir",false],[143,"scitex.path.find_dir",false]],"find_file() (in module scitex.path)":[[15,"scitex.path.find_file",false],[143,"scitex.path.find_file",false]],"find_git_root() (in module scitex.path)":[[15,"scitex.path.find_git_root",false],[143,"scitex.path.find_git_root",false]],"find_latest() (in module scitex.path)":[[15,"scitex.path.find_latest",false],[143,"scitex.path.find_latest",false]],"fix_broken_symlinks() (in module scitex.path)":[[15,"scitex.path.fix_broken_symlinks",false],[143,"scitex.path.fix_broken_symlinks",false]],"fix_mismatches() (in module scitex.dev)":[[120,"scitex.dev.fix_mismatches",false]],"fix_seeds() (in module scitex.repro)":[[18,"scitex.repro.fix_seeds",false],[147,"scitex.repro.fix_seeds",false]],"fix_seeds() (in module scitex.rng)":[[149,"scitex.rng.fix_seeds",false]],"flat (scitex.config.scitexconfig property)":[[3,"scitex.config.ScitexConfig.flat",false],[112,"scitex.config.ScitexConfig.flat",false]],"flip() (in module scitex.cv)":[[115,"scitex.cv.flip",false]],"flush() (scitex.logging.tee method)":[[13,"scitex.logging.Tee.flush",false],[134,"scitex.logging.Tee.flush",false]],"format_alerts_report() (in module scitex.security)":[[152,"scitex.security.format_alerts_report",false]],"format_python_signature() (in module scitex.cli)":[[109,"scitex.cli.format_python_signature",false]],"function_cache (scitex.config.scitexpaths property)":[[3,"scitex.config.ScitexPaths.function_cache",false],[112,"scitex.config.ScitexPaths.function_cache",false]],"gen_id() (in module scitex.repro)":[[18,"scitex.repro.gen_ID",false],[18,"scitex.repro.gen_id",false],[147,"scitex.repro.gen_ID",false],[147,"scitex.repro.gen_id",false]],"gen_id() (in module scitex.rng)":[[149,"scitex.rng.gen_ID",false],[149,"scitex.rng.gen_id",false]],"gen_timestamp() (in module scitex.repro)":[[18,"scitex.repro.gen_timestamp",false],[147,"scitex.repro.gen_timestamp",false]],"gen_timestamp() (in module scitex.rng)":[[149,"scitex.rng.gen_timestamp",false]],"get() (in module scitex.repro)":[[18,"scitex.repro.get",false],[147,"scitex.repro.get",false]],"get() (in module scitex.rng)":[[149,"scitex.rng.get",false]],"get() (scitex.config.priorityconfig method)":[[3,"scitex.config.PriorityConfig.get",false],[112,"scitex.config.PriorityConfig.get",false]],"get() (scitex.config.scitexconfig method)":[[3,"scitex.config.ScitexConfig.get",false],[112,"scitex.config.ScitexConfig.get",false]],"get_code_cells() (in module scitex.notebook)":[[139,"scitex.notebook.get_code_cells",false]],"get_component() (in module scitex.ui)":[[163,"scitex.ui.get_component",false]],"get_config() (in module scitex.config)":[[3,"scitex.config.get_config",false],[112,"scitex.config.get_config",false]],"get_data_path_from_a_package() (in module scitex.path)":[[15,"scitex.path.get_data_path_from_a_package",false],[143,"scitex.path.get_data_path_from_a_package",false]],"get_docs() (in module scitex.dev)":[[120,"scitex.dev.get_docs",false]],"get_docs_path() (in module scitex.ui)":[[163,"scitex.ui.get_docs_path",false]],"get_ecosystem_versions() (in module scitex.dev)":[[120,"scitex.dev.get_ecosystem_versions",false]],"get_files() (in module scitex.app)":[[100,"scitex.app.get_files",false]],"get_generator() (scitex.repro.randomstatemanager method)":[[18,"scitex.repro.RandomStateManager.get_generator",false],[147,"scitex.repro.RandomStateManager.get_generator",false]],"get_generator() (scitex.rng.randomstatemanager method)":[[149,"scitex.rng.RandomStateManager.get_generator",false]],"get_host_config() (in module scitex.resource)":[[148,"scitex.resource.get_host_config",false]],"get_host_name() (in module scitex.resource)":[[148,"scitex.resource.get_host_name",false]],"get_latest_alerts_file() (in module scitex.security)":[[152,"scitex.security.get_latest_alerts_file",false]],"get_level() (in module scitex.logging)":[[13,"scitex.logging.get_level",false],[134,"scitex.logging.get_level",false]],"get_log_path() (in module scitex.logging)":[[13,"scitex.logging.get_log_path",false],[134,"scitex.logging.get_log_path",false]],"get_machine_config() (in module scitex.resource)":[[148,"scitex.resource.get_machine_config",false]],"get_machine_name() (in module scitex.resource)":[[148,"scitex.resource.get_machine_name",false]],"get_metrics() (in module scitex.resource)":[[148,"scitex.resource.get_metrics",false]],"get_mismatches() (in module scitex.dev)":[[120,"scitex.dev.get_mismatches",false]],"get_nested() (scitex.config.scitexconfig method)":[[3,"scitex.config.ScitexConfig.get_nested",false],[112,"scitex.config.ScitexConfig.get_nested",false]],"get_notebook_directory() (in module scitex.context)":[[114,"scitex.context.get_notebook_directory",false]],"get_notebook_info_simple() (in module scitex.context)":[[114,"scitex.context.get_notebook_info_simple",false]],"get_notebook_name() (in module scitex.context)":[[114,"scitex.context.get_notebook_name",false]],"get_notebook_name() (in module scitex.notebook)":[[139,"scitex.notebook.get_notebook_name",false]],"get_notebook_path() (in module scitex.context)":[[114,"scitex.context.get_notebook_path",false]],"get_np_generator() (scitex.repro.randomstatemanager method)":[[18,"scitex.repro.RandomStateManager.get_np_generator",false],[147,"scitex.repro.RandomStateManager.get_np_generator",false]],"get_np_generator() (scitex.rng.randomstatemanager method)":[[149,"scitex.rng.RandomStateManager.get_np_generator",false]],"get_output_directory() (in module scitex.context)":[[114,"scitex.context.get_output_directory",false]],"get_paths() (in module scitex.config)":[[3,"scitex.config.get_paths",false],[112,"scitex.config.get_paths",false]],"get_pdf() (scitex.writer.writer method)":[[25,"scitex.writer.Writer.get_pdf",false],[166,"scitex.writer.Writer.get_pdf",false]],"get_processor_usages() (in module scitex.resource)":[[148,"scitex.resource.get_processor_usages",false]],"get_profile() (in module scitex.msword)":[[137,"scitex.msword.get_profile",false]],"get_scitex_dir() (in module scitex.config)":[[3,"scitex.config.get_scitex_dir",false],[112,"scitex.config.get_scitex_dir",false]],"get_sklearn_random_state() (scitex.repro.randomstatemanager method)":[[18,"scitex.repro.RandomStateManager.get_sklearn_random_state",false],[147,"scitex.repro.RandomStateManager.get_sklearn_random_state",false]],"get_sklearn_random_state() (scitex.rng.randomstatemanager method)":[[149,"scitex.rng.RandomStateManager.get_sklearn_random_state",false]],"get_spath() (in module scitex.path)":[[15,"scitex.path.get_spath",false],[143,"scitex.path.get_spath",false]],"get_specs() (in module scitex.resource)":[[148,"scitex.resource.get_specs",false]],"get_static_dir() (in module scitex.ui)":[[163,"scitex.ui.get_static_dir",false]],"get_this_path() (in module scitex.path)":[[15,"scitex.path.get_this_path",false],[143,"scitex.path.get_this_path",false]],"get_torch_generator() (scitex.repro.randomstatemanager method)":[[18,"scitex.repro.RandomStateManager.get_torch_generator",false],[147,"scitex.repro.RandomStateManager.get_torch_generator",false]],"get_torch_generator() (scitex.rng.randomstatemanager method)":[[149,"scitex.rng.RandomStateManager.get_torch_generator",false]],"getlogger() (in module scitex.logging)":[[13,"scitex.logging.getLogger",false],[134,"scitex.logging.getLogger",false]],"getsize() (in module scitex.path)":[[15,"scitex.path.getsize",false],[143,"scitex.path.getsize",false]],"git_root (scitex.writer.manuscripttree attribute)":[[25,"scitex.writer.ManuscriptTree.git_root",false],[166,"scitex.writer.ManuscriptTree.git_root",false]],"git_root (scitex.writer.revisiontree attribute)":[[25,"scitex.writer.RevisionTree.git_root",false],[166,"scitex.writer.RevisionTree.git_root",false]],"git_root (scitex.writer.supplementarytree attribute)":[[25,"scitex.writer.SupplementaryTree.git_root",false],[166,"scitex.writer.SupplementaryTree.git_root",false]],"githubsecurityerror":[[152,"scitex.security.GitHubSecurityError",false]],"group_to_json() (in module scitex.cli)":[[109,"scitex.cli.group_to_json",false]],"handle_result() (in module scitex.dev)":[[120,"scitex.dev.handle_result",false]],"hash_array() (in module scitex.repro)":[[18,"scitex.repro.hash_array",false],[147,"scitex.repro.hash_array",false]],"hash_array() (in module scitex.rng)":[[149,"scitex.rng.hash_array",false]],"hash_directory() (in module scitex.clew)":[[2,"scitex.clew.hash_directory",false],[108,"scitex.clew.hash_directory",false]],"hash_file() (in module scitex.clew)":[[2,"scitex.clew.hash_file",false],[108,"scitex.clew.hash_file",false]],"heading_background_hex (scitex.msword.basewordprofile attribute)":[[137,"scitex.msword.BaseWordProfile.heading_background_hex",false]],"heading_styles (scitex.msword.basewordprofile attribute)":[[137,"id2",false],[137,"scitex.msword.BaseWordProfile.heading_styles",false]],"help_recursive_to_json() (in module scitex.cli)":[[109,"scitex.cli.help_recursive_to_json",false]],"hide_spines() (ax method)":[[145,"ax.hide_spines",false]],"hints_on_error (scitex.dev.result attribute)":[[120,"scitex.dev.Result.hints_on_error",false]],"hints_on_success (scitex.dev.result attribute)":[[120,"scitex.dev.Result.hints_on_success",false]],"hints_on_warning (scitex.dev.result attribute)":[[120,"scitex.dev.Result.hints_on_warning",false]],"hist() (ax method)":[[145,"ax.hist",false]],"idempotent (scitex.dev.result attribute)":[[120,"scitex.dev.Result.idempotent",false]],"impact_factor_cache (scitex.config.scitexpaths property)":[[3,"scitex.config.ScitexPaths.impact_factor_cache",false],[112,"scitex.config.ScitexPaths.impact_factor_cache",false]],"increment_version() (in module scitex.path)":[[15,"scitex.path.increment_version",false],[143,"scitex.path.increment_version",false]],"init_examples() (in module scitex.clew)":[[2,"scitex.clew.init_examples",false],[108,"scitex.clew.init_examples",false]],"installhint (class in scitex.dev)":[[120,"scitex.dev.InstallHint",false]],"invalidpatherror":[[13,"scitex.logging.InvalidPathError",false],[134,"scitex.logging.InvalidPathError",false]],"ioerror":[[13,"scitex.logging.IOError",false],[134,"scitex.logging.IOError",false]],"is_file_logging_enabled() (in module scitex.logging)":[[13,"scitex.logging.is_file_logging_enabled",false],[134,"scitex.logging.is_file_logging_enabled",false]],"is_host() (in module scitex.os)":[[141,"scitex.os.is_host",false]],"is_ipython() (in module scitex.context)":[[114,"scitex.context.is_ipython",false]],"is_notebook() (in module scitex.context)":[[114,"scitex.context.is_notebook",false]],"is_script() (in module scitex.context)":[[114,"scitex.context.is_script",false]],"is_symlink() (in module scitex.path)":[[15,"scitex.path.is_symlink",false],[143,"scitex.path.is_symlink",false]],"is_track_changes_enabled() (in module scitex.msword)":[[137,"scitex.msword.is_track_changes_enabled",false]],"isatty() (scitex.logging.tee method)":[[13,"scitex.logging.Tee.isatty",false],[134,"scitex.logging.Tee.isatty",false]],"json_option() (in module scitex.dev)":[[120,"scitex.dev.json_option",false]],"json_schema() (scitex.dev.result class method)":[[120,"scitex.dev.Result.json_schema",false]],"last_install_hint() (in module scitex.dev)":[[120,"scitex.dev.last_install_hint",false]],"line() (in module scitex.cv)":[[115,"scitex.cv.line",false]],"line_spacing (scitex.msword.basewordprofile attribute)":[[137,"scitex.msword.BaseWordProfile.line_spacing",false]],"link_captions_to_images() (in module scitex.msword)":[[137,"scitex.msword.link_captions_to_images",false]],"link_captions_to_images_by_proximity() (in module scitex.msword)":[[137,"scitex.msword.link_captions_to_images_by_proximity",false]],"list() (scitex.app.filesbackend method)":[[100,"scitex.app.FilesBackend.list",false]],"list_all() (scitex.config.scitexpaths method)":[[3,"scitex.config.ScitexPaths.list_all",false],[112,"scitex.config.ScitexPaths.list_all",false]],"list_claims() (in module scitex.clew)":[[2,"scitex.clew.list_claims",false],[108,"scitex.clew.list_claims",false]],"list_components() (in module scitex.ui)":[[163,"scitex.ui.list_components",false]],"list_files() (in module scitex.app)":[[100,"scitex.app.list_files",false]],"list_profiles() (in module scitex.msword)":[[137,"scitex.msword.list_profiles",false]],"list_runs() (in module scitex.clew)":[[2,"scitex.clew.list_runs",false],[108,"scitex.clew.list_runs",false]],"list_stamps() (in module scitex.clew)":[[2,"scitex.clew.list_stamps",false],[108,"scitex.clew.list_stamps",false]],"list_styles (scitex.msword.basewordprofile attribute)":[[137,"id8",false],[137,"scitex.msword.BaseWordProfile.list_styles",false]],"list_symlinks() (in module scitex.path)":[[15,"scitex.path.list_symlinks",false],[143,"scitex.path.list_symlinks",false]],"list_versions() (in module scitex.dev)":[[120,"scitex.dev.list_versions",false]],"load() (in module scitex.cv)":[[115,"scitex.cv.load",false]],"load_config() (in module scitex.resource)":[[148,"scitex.resource.load_config",false]],"load_docx() (in module scitex.msword)":[[137,"scitex.msword.load_docx",false]],"load_dotenv() (in module scitex.config)":[[3,"scitex.config.load_dotenv",false],[112,"scitex.config.load_dotenv",false]],"load_env_from_path() (in module scitex.config)":[[3,"scitex.config.load_env_from_path",false],[112,"scitex.config.load_env_from_path",false]],"load_scitex_env() (in module scitex.config)":[[3,"scitex.config.load_scitex_env",false],[112,"scitex.config.load_scitex_env",false]],"load_yaml() (in module scitex.config)":[[3,"scitex.config.load_yaml",false],[112,"scitex.config.load_yaml",false]],"loaderror":[[13,"scitex.logging.LoadError",false],[134,"scitex.logging.LoadError",false]],"log_file (scitex.writer.compilationresult attribute)":[[25,"scitex.writer.CompilationResult.log_file",false],[166,"scitex.writer.CompilationResult.log_file",false]],"log_processor_usages() (in module scitex.resource)":[[148,"scitex.resource.log_processor_usages",false]],"log_to_file() (in module scitex.logging)":[[13,"scitex.logging.log_to_file",false],[134,"scitex.logging.log_to_file",false]],"logs (scitex.config.scitexpaths property)":[[3,"scitex.config.ScitexPaths.logs",false],[112,"scitex.config.ScitexPaths.logs",false]],"main() (in module scitex.resource)":[[148,"scitex.resource.main",false]],"manuscripttree (class in scitex.writer)":[[25,"scitex.writer.ManuscriptTree",false],[166,"scitex.writer.ManuscriptTree",false]],"mark_additions() (in module scitex.msword)":[[137,"scitex.msword.mark_additions",false]],"mark_modifications() (in module scitex.msword)":[[137,"scitex.msword.mark_modifications",false]],"mermaid() (in module scitex.clew)":[[2,"scitex.clew.mermaid",false],[108,"scitex.clew.mermaid",false]],"mk_spath() (in module scitex.path)":[[15,"scitex.path.mk_spath",false],[143,"scitex.path.mk_spath",false]],"modelerror":[[13,"scitex.logging.ModelError",false],[134,"scitex.logging.ModelError",false]],"module":[[1,"module-scitex.ai",false],[2,"module-scitex.clew",false],[3,"module-scitex.config",false],[4,"module-scitex.db",false],[5,"module-scitex.decorators",false],[6,"module-scitex.diagram",false],[7,"module-scitex.dict",false],[8,"module-scitex.dsp",false],[9,"module-scitex.gen",false],[10,"module-scitex.introspect",false],[11,"module-scitex.io",false],[13,"module-scitex.logging",false],[14,"module-scitex.nn",false],[15,"module-scitex.path",false],[16,"module-scitex.pd",false],[17,"module-scitex.plt",false],[18,"module-scitex.repro",false],[19,"module-scitex.scholar",false],[20,"module-scitex.session",false],[21,"module-scitex.social",false],[22,"module-scitex.stats",false],[23,"module-scitex.str",false],[24,"module-scitex.template",false],[25,"module-scitex.writer",false],[99,"module-scitex.ai",false],[100,"module-scitex.app",false],[101,"module-scitex.audio",false],[102,"module-scitex.audit",false],[103,"module-scitex.benchmark",false],[104,"module-scitex.bridge",false],[105,"module-scitex.browser",false],[106,"module-scitex.canvas",false],[107,"module-scitex.capture",false],[108,"module-scitex.clew",false],[109,"module-scitex.cli",false],[110,"module-scitex.cloud",false],[111,"module-scitex.compat",false],[112,"module-scitex.config",false],[113,"module-scitex.container",false],[114,"module-scitex.context",false],[115,"module-scitex.cv",false],[116,"module-scitex.dataset",false],[117,"module-scitex.datetime",false],[118,"module-scitex.db",false],[119,"module-scitex.decorators",false],[120,"module-scitex.dev",false],[121,"module-scitex.diagram",false],[122,"module-scitex.dict",false],[123,"module-scitex.dsp",false],[124,"module-scitex.etc",false],[125,"module-scitex.events",false],[126,"module-scitex.gen",false],[127,"module-scitex.gists",false],[128,"module-scitex.git",false],[130,"module-scitex.introspect",false],[131,"module-scitex.io",false],[132,"module-scitex.linalg",false],[134,"module-scitex.logging",false],[135,"module-scitex.media",false],[136,"module-scitex.module",false],[137,"module-scitex.msword",false],[138,"module-scitex.nn",false],[139,"module-scitex.notebook",false],[140,"module-scitex.notification",false],[141,"module-scitex.os",false],[142,"module-scitex.parallel",false],[143,"module-scitex.path",false],[144,"module-scitex.pd",false],[146,"module-scitex.project",false],[147,"module-scitex.repro",false],[148,"module-scitex.resource",false],[149,"module-scitex.rng",false],[150,"module-scitex.schema",false],[151,"module-scitex.scholar",false],[152,"module-scitex.security",false],[153,"module-scitex.session",false],[154,"module-scitex.sh",false],[155,"module-scitex.social",false],[156,"module-scitex.stats",false],[157,"module-scitex.str",false],[158,"module-scitex.template",false],[159,"module-scitex.tex",false],[160,"module-scitex.torch",false],[161,"module-scitex.tunnel",false],[162,"module-scitex.types",false],[163,"module-scitex.ui",false],[165,"module-scitex.web",false],[166,"module-scitex.writer",false]],"mv() (in module scitex.os)":[[141,"scitex.os.mv",false]],"name (scitex.msword.basewordprofile attribute)":[[137,"id0",false],[137,"scitex.msword.BaseWordProfile.name",false]],"nnerror":[[13,"scitex.logging.NNError",false],[134,"scitex.logging.NNError",false]],"normal_style (scitex.msword.basewordprofile attribute)":[[137,"id4",false],[137,"scitex.msword.BaseWordProfile.normal_style",false]],"normalize_section_headings() (in module scitex.msword)":[[137,"scitex.msword.normalize_section_headings",false]],"notebook_path (scitex.notebook.compilednotebook attribute)":[[139,"id0",false],[139,"scitex.notebook.CompiledNotebook.notebook_path",false]],"notify() (in module scitex.compat)":[[111,"scitex.compat.notify",false]],"notify_async() (in module scitex.compat)":[[111,"scitex.compat.notify_async",false]],"on_session_close() (in module scitex.clew)":[[2,"scitex.clew.on_session_close",false],[108,"scitex.clew.on_session_close",false]],"on_session_start() (in module scitex.clew)":[[2,"scitex.clew.on_session_start",false],[108,"scitex.clew.on_session_start",false]],"openathens_cache (scitex.config.scitexpaths property)":[[3,"scitex.config.ScitexPaths.openathens_cache",false],[112,"scitex.config.ScitexPaths.openathens_cache",false]],"output_pdf (scitex.writer.compilationresult attribute)":[[25,"scitex.writer.CompilationResult.output_pdf",false],[166,"scitex.writer.CompilationResult.output_pdf",false]],"pad() (in module scitex.cv)":[[115,"scitex.cv.pad",false]],"parse_notebook() (in module scitex.notebook)":[[139,"scitex.notebook.parse_notebook",false]],"parse_src_file() (in module scitex.config)":[[3,"scitex.config.parse_src_file",false],[112,"scitex.config.parse_src_file",false]],"patherror":[[13,"scitex.logging.PathError",false],[134,"scitex.logging.PathError",false]],"pathnotfounderror":[[13,"scitex.logging.PathNotFoundError",false],[134,"scitex.logging.PathNotFoundError",false]],"pdfdownloaderror":[[13,"scitex.logging.PDFDownloadError",false],[134,"scitex.logging.PDFDownloadError",false]],"pdfextractionerror":[[13,"scitex.logging.PDFExtractionError",false],[134,"scitex.logging.PDFExtractionError",false]],"performancewarning":[[13,"scitex.logging.PerformanceWarning",false],[134,"scitex.logging.PerformanceWarning",false]],"plottingerror":[[13,"scitex.logging.PlottingError",false],[134,"scitex.logging.PlottingError",false]],"polylines() (in module scitex.cv)":[[115,"scitex.cv.polylines",false]],"post_import_hooks (scitex.msword.basewordprofile attribute)":[[137,"scitex.msword.BaseWordProfile.post_import_hooks",false]],"pre_export_hooks (scitex.msword.basewordprofile attribute)":[[137,"scitex.msword.BaseWordProfile.pre_export_hooks",false]],"preserve_bold_tokens() (in module scitex.msword)":[[137,"scitex.msword.preserve_bold_tokens",false]],"print() (scitex.config.scitexconfig method)":[[3,"scitex.config.ScitexConfig.print",false],[112,"scitex.config.ScitexConfig.print",false]],"print_help_recursive() (in module scitex.cli)":[[109,"scitex.cli.print_help_recursive",false]],"print_resolutions() (scitex.config.priorityconfig method)":[[3,"scitex.config.PriorityConfig.print_resolutions",false],[112,"scitex.config.PriorityConfig.print_resolutions",false]],"priorityconfig (class in scitex.config)":[[3,"scitex.config.PriorityConfig",false],[112,"scitex.config.PriorityConfig",false]],"pull_local() (in module scitex.dev)":[[120,"scitex.dev.pull_local",false]],"quiet() (in module scitex.context)":[[114,"scitex.context.quiet",false]],"randomstatemanager (class in scitex.repro)":[[18,"scitex.repro.RandomStateManager",false],[147,"scitex.repro.RandomStateManager",false]],"randomstatemanager (class in scitex.rng)":[[149,"scitex.rng.RandomStateManager",false]],"raw (scitex.config.scitexconfig property)":[[3,"scitex.config.ScitexConfig.raw",false],[112,"scitex.config.ScitexConfig.raw",false]],"read() (scitex.app.filesbackend method)":[[100,"scitex.app.FilesBackend.read",false]],"read() (scitex.msword.wordreader method)":[[137,"scitex.msword.WordReader.read",false]],"read_file() (in module scitex.app)":[[100,"scitex.app.read_file",false]],"readlink() (in module scitex.path)":[[15,"scitex.path.readlink",false],[143,"scitex.path.readlink",false]],"readme (scitex.writer.manuscripttree attribute)":[[25,"scitex.writer.ManuscriptTree.readme",false],[166,"scitex.writer.ManuscriptTree.readme",false]],"readme (scitex.writer.revisiontree attribute)":[[25,"scitex.writer.RevisionTree.readme",false],[166,"scitex.writer.RevisionTree.readme",false]],"readme (scitex.writer.supplementarytree attribute)":[[25,"scitex.writer.SupplementaryTree.readme",false],[166,"scitex.writer.SupplementaryTree.readme",false]],"rectangle() (in module scitex.cv)":[[115,"scitex.cv.rectangle",false]],"reference_section_titles (scitex.msword.basewordprofile attribute)":[[137,"id5",false],[137,"scitex.msword.BaseWordProfile.reference_section_titles",false]],"register_backend() (in module scitex.app)":[[100,"scitex.app.register_backend",false]],"register_intermediate() (in module scitex.clew)":[[2,"scitex.clew.register_intermediate",false],[108,"scitex.clew.register_intermediate",false]],"register_profile() (in module scitex.msword)":[[137,"scitex.msword.register_profile",false]],"reject_all_tracked_changes() (in module scitex.msword)":[[137,"scitex.msword.reject_all_tracked_changes",false]],"remote_commit() (in module scitex.dev)":[[120,"scitex.dev.remote_commit",false]],"remote_diff() (in module scitex.dev)":[[120,"scitex.dev.remote_diff",false]],"rename() (scitex.app.filesbackend method)":[[100,"scitex.app.FilesBackend.rename",false]],"rename_file() (in module scitex.app)":[[100,"scitex.app.rename_file",false]],"rerun() (in module scitex.clew)":[[2,"scitex.clew.rerun",false],[108,"scitex.clew.rerun",false]],"rerun_claims() (in module scitex.clew)":[[2,"scitex.clew.rerun_claims",false],[108,"scitex.clew.rerun_claims",false]],"rerun_dag() (in module scitex.clew)":[[2,"scitex.clew.rerun_dag",false],[108,"scitex.clew.rerun_dag",false]],"reset() (in module scitex.repro)":[[18,"scitex.repro.reset",false],[147,"scitex.repro.reset",false]],"reset() (in module scitex.rng)":[[149,"scitex.rng.reset",false]],"resetwarnings() (in module scitex.logging)":[[13,"scitex.logging.resetwarnings",false],[134,"scitex.logging.resetwarnings",false]],"resize() (in module scitex.cv)":[[115,"scitex.cv.resize",false]],"resolution_log (scitex.config.priorityconfig attribute)":[[112,"scitex.config.PriorityConfig.resolution_log",false]],"resolve() (scitex.config.priorityconfig method)":[[3,"scitex.config.PriorityConfig.resolve",false],[112,"scitex.config.PriorityConfig.resolve",false]],"resolve() (scitex.config.scitexconfig method)":[[3,"scitex.config.ScitexConfig.resolve",false],[112,"scitex.config.ScitexConfig.resolve",false]],"resolve() (scitex.config.scitexpaths method)":[[3,"scitex.config.ScitexPaths.resolve",false],[112,"scitex.config.ScitexPaths.resolve",false]],"resolve_symlinks() (in module scitex.path)":[[15,"scitex.path.resolve_symlinks",false],[143,"scitex.path.resolve_symlinks",false]],"restore() (scitex.repro.randomstatemanager method)":[[18,"scitex.repro.RandomStateManager.restore",false],[147,"scitex.repro.RandomStateManager.restore",false]],"restore() (scitex.rng.randomstatemanager method)":[[149,"scitex.rng.RandomStateManager.restore",false]],"result (class in scitex.dev)":[[120,"scitex.dev.Result",false]],"result_to_mcp() (in module scitex.dev)":[[120,"scitex.dev.result_to_mcp",false]],"revision (scitex.writer.revisiontree attribute)":[[25,"scitex.writer.RevisionTree.revision",false],[166,"scitex.writer.RevisionTree.revision",false]],"revisiontree (class in scitex.writer)":[[25,"scitex.writer.RevisionTree",false],[166,"scitex.writer.RevisionTree",false]],"rng (scitex.config.scitexpaths property)":[[3,"scitex.config.ScitexPaths.rng",false],[112,"scitex.config.ScitexPaths.rng",false]],"root (scitex.writer.manuscripttree attribute)":[[25,"scitex.writer.ManuscriptTree.root",false],[166,"scitex.writer.ManuscriptTree.root",false]],"root (scitex.writer.revisiontree attribute)":[[25,"scitex.writer.RevisionTree.root",false],[166,"scitex.writer.RevisionTree.root",false]],"root (scitex.writer.supplementarytree attribute)":[[25,"scitex.writer.SupplementaryTree.root",false],[166,"scitex.writer.SupplementaryTree.root",false]],"rotate() (in module scitex.cv)":[[115,"scitex.cv.rotate",false]],"run() (in module scitex.clew)":[[2,"scitex.clew.run",false],[108,"scitex.clew.run",false]],"run() (in module scitex.parallel)":[[142,"scitex.parallel.run",false]],"run_as_cli() (in module scitex.dev)":[[120,"scitex.dev.run_as_cli",false]],"run_as_mcp() (in module scitex.dev)":[[120,"scitex.dev.run_as_mcp",false]],"runs (scitex.notebook.compilednotebook attribute)":[[139,"id3",false],[139,"scitex.notebook.CompiledNotebook.runs",false]],"save() (in module scitex.cv)":[[115,"scitex.cv.save",false]],"save_alerts_to_file() (in module scitex.security)":[[152,"scitex.security.save_alerts_to_file",false]],"save_docx() (in module scitex.msword)":[[137,"scitex.msword.save_docx",false]],"saveerror":[[13,"scitex.logging.SaveError",false],[134,"scitex.logging.SaveError",false]],"scaffold() (in module scitex.app)":[[100,"scitex.app.scaffold",false]],"scholar (scitex.config.scitexpaths property)":[[3,"scitex.config.ScitexPaths.scholar",false],[112,"scitex.config.ScitexPaths.scholar",false]],"scholar_cache (scitex.config.scitexpaths property)":[[3,"scitex.config.ScitexPaths.scholar_cache",false],[112,"scitex.config.ScitexPaths.scholar_cache",false]],"scholar_library (scitex.config.scitexpaths property)":[[3,"scitex.config.ScitexPaths.scholar_library",false],[112,"scitex.config.ScitexPaths.scholar_library",false]],"scholarerror":[[13,"scitex.logging.ScholarError",false],[134,"scitex.logging.ScholarError",false]],"scitex.ai":[[1,"module-scitex.ai",false],[99,"module-scitex.ai",false]],"scitex.app":[[100,"module-scitex.app",false]],"scitex.audio":[[101,"module-scitex.audio",false]],"scitex.audit":[[102,"module-scitex.audit",false]],"scitex.benchmark":[[103,"module-scitex.benchmark",false]],"scitex.bridge":[[104,"module-scitex.bridge",false]],"scitex.browser":[[105,"module-scitex.browser",false]],"scitex.canvas":[[106,"module-scitex.canvas",false]],"scitex.capture":[[107,"module-scitex.capture",false]],"scitex.clew":[[2,"module-scitex.clew",false],[108,"module-scitex.clew",false]],"scitex.cli":[[109,"module-scitex.cli",false]],"scitex.cloud":[[110,"module-scitex.cloud",false]],"scitex.compat":[[111,"module-scitex.compat",false]],"scitex.config":[[3,"module-scitex.config",false],[112,"module-scitex.config",false]],"scitex.container":[[113,"module-scitex.container",false]],"scitex.context":[[114,"module-scitex.context",false]],"scitex.cv":[[115,"module-scitex.cv",false]],"scitex.dataset":[[116,"module-scitex.dataset",false]],"scitex.datetime":[[117,"module-scitex.datetime",false]],"scitex.db":[[4,"module-scitex.db",false],[118,"module-scitex.db",false]],"scitex.decorators":[[5,"module-scitex.decorators",false],[119,"module-scitex.decorators",false]],"scitex.dev":[[120,"module-scitex.dev",false]],"scitex.diagram":[[6,"module-scitex.diagram",false],[121,"module-scitex.diagram",false]],"scitex.dict":[[7,"module-scitex.dict",false],[122,"module-scitex.dict",false]],"scitex.dsp":[[8,"module-scitex.dsp",false],[123,"module-scitex.dsp",false]],"scitex.etc":[[124,"module-scitex.etc",false]],"scitex.events":[[125,"module-scitex.events",false]],"scitex.gen":[[9,"module-scitex.gen",false],[126,"module-scitex.gen",false]],"scitex.gists":[[127,"module-scitex.gists",false]],"scitex.git":[[128,"module-scitex.git",false]],"scitex.introspect":[[10,"module-scitex.introspect",false],[130,"module-scitex.introspect",false]],"scitex.io":[[11,"module-scitex.io",false],[131,"module-scitex.io",false]],"scitex.linalg":[[132,"module-scitex.linalg",false]],"scitex.logging":[[13,"module-scitex.logging",false],[134,"module-scitex.logging",false]],"scitex.media":[[135,"module-scitex.media",false]],"scitex.module":[[136,"module-scitex.module",false]],"scitex.msword":[[137,"module-scitex.msword",false]],"scitex.nn":[[14,"module-scitex.nn",false],[138,"module-scitex.nn",false]],"scitex.notebook":[[139,"module-scitex.notebook",false]],"scitex.notification":[[140,"module-scitex.notification",false]],"scitex.os":[[141,"module-scitex.os",false]],"scitex.parallel":[[142,"module-scitex.parallel",false]],"scitex.path":[[15,"module-scitex.path",false],[143,"module-scitex.path",false]],"scitex.pd":[[16,"module-scitex.pd",false],[144,"module-scitex.pd",false]],"scitex.plt":[[17,"module-scitex.plt",false]],"scitex.project":[[146,"module-scitex.project",false]],"scitex.repro":[[18,"module-scitex.repro",false],[147,"module-scitex.repro",false]],"scitex.resource":[[148,"module-scitex.resource",false]],"scitex.rng":[[149,"module-scitex.rng",false]],"scitex.schema":[[150,"module-scitex.schema",false]],"scitex.scholar":[[19,"module-scitex.scholar",false],[151,"module-scitex.scholar",false]],"scitex.security":[[152,"module-scitex.security",false]],"scitex.session":[[20,"module-scitex.session",false],[153,"module-scitex.session",false]],"scitex.sh":[[154,"module-scitex.sh",false]],"scitex.social":[[21,"module-scitex.social",false],[155,"module-scitex.social",false]],"scitex.stats":[[22,"module-scitex.stats",false],[156,"module-scitex.stats",false]],"scitex.str":[[23,"module-scitex.str",false],[157,"module-scitex.str",false]],"scitex.template":[[24,"module-scitex.template",false],[158,"module-scitex.template",false]],"scitex.tex":[[159,"module-scitex.tex",false]],"scitex.torch":[[160,"module-scitex.torch",false]],"scitex.tunnel":[[161,"module-scitex.tunnel",false]],"scitex.types":[[162,"module-scitex.types",false]],"scitex.ui":[[163,"module-scitex.ui",false]],"scitex.web":[[165,"module-scitex.web",false]],"scitex.writer":[[25,"module-scitex.writer",false],[166,"module-scitex.writer",false]],"scitexconfig (class in scitex.config)":[[3,"scitex.config.ScitexConfig",false],[112,"scitex.config.ScitexConfig",false]],"scitexdeprecationwarning":[[13,"scitex.logging.SciTeXDeprecationWarning",false],[134,"scitex.logging.SciTeXDeprecationWarning",false]],"scitexerror":[[13,"scitex.logging.SciTeXError",false],[120,"scitex.dev.ScitexError",false],[134,"scitex.logging.SciTeXError",false]],"scitexpaths (class in scitex.config)":[[3,"scitex.config.ScitexPaths",false],[112,"scitex.config.ScitexPaths",false]],"scitexwarning":[[13,"scitex.logging.SciTeXWarning",false],[134,"scitex.logging.SciTeXWarning",false]],"screenshots (scitex.config.scitexpaths property)":[[3,"scitex.config.ScitexPaths.screenshots",false],[112,"scitex.config.ScitexPaths.screenshots",false]],"search() (in module scitex.dev)":[[120,"scitex.dev.search",false]],"search() (in module scitex.etc)":[[124,"scitex.etc.search",false]],"search_docs() (in module scitex.dev)":[[120,"scitex.dev.search_docs",false]],"searcherror":[[13,"scitex.logging.SearchError",false],[134,"scitex.logging.SearchError",false]],"sensitive_expressions (scitex.config.priorityconfig attribute)":[[3,"scitex.config.PriorityConfig.SENSITIVE_EXPRESSIONS",false],[112,"scitex.config.PriorityConfig.SENSITIVE_EXPRESSIONS",false]],"set_level() (in module scitex.logging)":[[13,"scitex.logging.set_level",false],[134,"scitex.logging.set_level",false]],"set_xyt() (ax method)":[[145,"ax.set_xyt",false]],"shapeerror":[[13,"scitex.logging.ShapeError",false],[134,"scitex.logging.ShapeError",false]],"sharpen() (in module scitex.cv)":[[115,"scitex.cv.sharpen",false]],"side_effects (scitex.dev.result attribute)":[[120,"scitex.dev.Result.side_effects",false]],"sideeffect (class in scitex.dev)":[[120,"scitex.dev.SideEffect",false]],"sigmacro_process_figure_s() (in module scitex.gists)":[[127,"scitex.gists.sigmacro_process_figure_s",false]],"sigmacro_processfigure_s() (in module scitex.gists)":[[127,"scitex.gists.SigMacro_processFigure_S",false]],"sigmacro_to_blue() (in module scitex.gists)":[[127,"scitex.gists.sigmacro_to_blue",false]],"sigmacro_toblue() (in module scitex.gists)":[[127,"scitex.gists.SigMacro_toBlue",false]],"sns_barplot() (ax method)":[[145,"ax.sns_barplot",false]],"sns_boxplot() (ax method)":[[145,"ax.sns_boxplot",false]],"sns_heatmap() (ax method)":[[145,"ax.sns_heatmap",false]],"sns_histplot() (ax method)":[[145,"ax.sns_histplot",false]],"sns_jointplot() (ax method)":[[145,"ax.sns_jointplot",false]],"sns_kdeplot() (ax method)":[[145,"ax.sns_kdeplot",false]],"sns_lineplot() (ax method)":[[145,"ax.sns_lineplot",false]],"sns_pairplot() (ax method)":[[145,"ax.sns_pairplot",false]],"sns_scatterplot() (ax method)":[[145,"ax.sns_scatterplot",false]],"sns_stripplot() (ax method)":[[145,"ax.sns_stripplot",false]],"sns_swarmplot() (ax method)":[[145,"ax.sns_swarmplot",false]],"sns_violinplot() (ax method)":[[145,"ax.sns_violinplot",false]],"split() (in module scitex.path)":[[15,"scitex.path.split",false],[143,"scitex.path.split",false]],"stamp() (in module scitex.clew)":[[2,"scitex.clew.stamp",false],[108,"scitex.clew.stamp",false]],"stats() (in module scitex.clew)":[[2,"scitex.clew.stats",false],[108,"scitex.clew.stats",false]],"statserror":[[13,"scitex.logging.StatsError",false],[134,"scitex.logging.StatsError",false]],"status() (in module scitex.clew)":[[2,"scitex.clew.status",false],[108,"scitex.clew.status",false]],"stderr (scitex.writer.compilationresult attribute)":[[25,"scitex.writer.CompilationResult.stderr",false],[166,"scitex.writer.CompilationResult.stderr",false]],"stdout (scitex.writer.compilationresult attribute)":[[25,"scitex.writer.CompilationResult.stdout",false],[166,"scitex.writer.CompilationResult.stdout",false]],"stx_bar() (ax method)":[[145,"ax.stx_bar",false]],"stx_barh() (ax method)":[[145,"ax.stx_barh",false]],"stx_box() (ax method)":[[145,"ax.stx_box",false]],"stx_conf_mat() (ax method)":[[145,"ax.stx_conf_mat",false]],"stx_ecdf() (ax method)":[[145,"ax.stx_ecdf",false]],"stx_errorbar() (ax method)":[[145,"ax.stx_errorbar",false]],"stx_fillv() (ax method)":[[145,"ax.stx_fillv",false]],"stx_heatmap() (ax method)":[[145,"ax.stx_heatmap",false]],"stx_image() (ax method)":[[145,"ax.stx_image",false]],"stx_joyplot() (ax method)":[[145,"ax.stx_joyplot",false]],"stx_kde() (ax method)":[[145,"ax.stx_kde",false]],"stx_line() (ax method)":[[145,"ax.stx_line",false]],"stx_mean_ci() (ax method)":[[145,"ax.stx_mean_ci",false]],"stx_mean_std() (ax method)":[[145,"ax.stx_mean_std",false]],"stx_median_iqr() (ax method)":[[145,"ax.stx_median_iqr",false]],"stx_raster() (ax method)":[[145,"ax.stx_raster",false]],"stx_rectangle() (ax method)":[[145,"ax.stx_rectangle",false]],"stx_scatter() (ax method)":[[145,"ax.stx_scatter",false]],"stx_scatter_hist() (ax method)":[[145,"ax.stx_scatter_hist",false]],"stx_shaded_line() (ax method)":[[145,"ax.stx_shaded_line",false]],"stx_violin() (ax method)":[[145,"ax.stx_violin",false]],"stylewarning":[[13,"scitex.logging.StyleWarning",false],[134,"scitex.logging.StyleWarning",false]],"success (scitex.dev.result attribute)":[[120,"scitex.dev.Result.success",false]],"success (scitex.writer.compilationresult attribute)":[[25,"scitex.writer.CompilationResult.success",false],[166,"scitex.writer.CompilationResult.success",false]],"summarize_diff() (in module scitex.msword)":[[137,"scitex.msword.summarize_diff",false]],"supplementary (scitex.writer.supplementarytree attribute)":[[25,"scitex.writer.SupplementaryTree.supplementary",false],[166,"scitex.writer.SupplementaryTree.supplementary",false]],"supplementary_diff (scitex.writer.supplementarytree attribute)":[[25,"scitex.writer.SupplementaryTree.supplementary_diff",false],[166,"scitex.writer.SupplementaryTree.supplementary_diff",false]],"supplementarytree (class in scitex.writer)":[[25,"scitex.writer.SupplementaryTree",false],[166,"scitex.writer.SupplementaryTree",false]],"supports_return_as() (in module scitex.dev)":[[120,"scitex.dev.supports_return_as",false]],"suppress_output() (in module scitex.context)":[[114,"scitex.context.suppress_output",false]],"symlink() (in module scitex.path)":[[15,"scitex.path.symlink",false],[143,"scitex.path.symlink",false]],"sync_all() (in module scitex.dev)":[[120,"scitex.dev.sync_all",false]],"sync_host() (in module scitex.dev)":[[120,"scitex.dev.sync_host",false]],"sync_local() (in module scitex.dev)":[[120,"scitex.dev.sync_local",false]],"sync_tags() (in module scitex.dev)":[[120,"scitex.dev.sync_tags",false]],"table_caption_prefixes (scitex.msword.basewordprofile attribute)":[[137,"id7",false],[137,"scitex.msword.BaseWordProfile.table_caption_prefixes",false]],"target (scitex.dev.sideeffect attribute)":[[120,"scitex.dev.SideEffect.target",false]],"tee (class in scitex.logging)":[[13,"scitex.logging.Tee",false],[134,"scitex.logging.Tee",false]],"tee() (in module scitex.logging)":[[13,"scitex.logging.tee",false],[134,"scitex.logging.tee",false]],"templateerror":[[13,"scitex.logging.TemplateError",false],[134,"scitex.logging.TemplateError",false]],"templateviolationerror":[[13,"scitex.logging.TemplateViolationError",false],[134,"scitex.logging.TemplateViolationError",false]],"temporary_seed() (scitex.repro.randomstatemanager method)":[[18,"scitex.repro.RandomStateManager.temporary_seed",false],[147,"scitex.repro.RandomStateManager.temporary_seed",false]],"temporary_seed() (scitex.rng.randomstatemanager method)":[[149,"scitex.rng.RandomStateManager.temporary_seed",false]],"test_monitor (scitex.config.scitexpaths property)":[[3,"scitex.config.ScitexPaths.test_monitor",false],[112,"scitex.config.ScitexPaths.test_monitor",false]],"testerror":[[13,"scitex.logging.TestError",false],[134,"scitex.logging.TestError",false]],"text() (in module scitex.cv)":[[115,"scitex.cv.text",false]],"this_path() (in module scitex.path)":[[15,"scitex.path.this_path",false],[143,"scitex.path.this_path",false]],"threshold() (in module scitex.cv)":[[115,"scitex.cv.threshold",false]],"timestamp() (in module scitex.repro)":[[18,"scitex.repro.timestamp",false],[147,"scitex.repro.timestamp",false]],"timestamp() (in module scitex.rng)":[[149,"scitex.rng.timestamp",false]],"to_bgr() (in module scitex.cv)":[[115,"scitex.cv.to_bgr",false]],"to_dict() (scitex.dev.result method)":[[120,"scitex.dev.Result.to_dict",false]],"to_dict() (scitex.dev.scitexerror method)":[[120,"scitex.dev.ScitexError.to_dict",false]],"to_gray() (in module scitex.cv)":[[115,"scitex.cv.to_gray",false]],"to_json() (scitex.dev.result method)":[[120,"scitex.dev.Result.to_json",false]],"to_mermaid() (scitex.notebook.compilednotebook method)":[[139,"scitex.notebook.CompiledNotebook.to_mermaid",false]],"to_rgb() (in module scitex.cv)":[[115,"scitex.cv.to_rgb",false]],"to_script() (scitex.notebook.compilednotebook method)":[[139,"scitex.notebook.CompiledNotebook.to_script",false]],"translatorerror":[[13,"scitex.logging.TranslatorError",false],[134,"scitex.logging.TranslatorError",false]],"try_import_optional() (in module scitex.dev)":[[120,"scitex.dev.try_import_optional",false]],"type (scitex.dev.sideeffect attribute)":[[120,"scitex.dev.SideEffect.type",false]],"undoable (scitex.dev.sideeffect attribute)":[[120,"scitex.dev.SideEffect.undoable",false]],"unitwarning":[[13,"scitex.logging.UnitWarning",false],[134,"scitex.logging.UnitWarning",false]],"unlink_symlink() (in module scitex.path)":[[15,"scitex.path.unlink_symlink",false],[143,"scitex.path.unlink_symlink",false]],"usage() (in module scitex.writer)":[[25,"scitex.writer.usage",false],[166,"scitex.writer.usage",false]],"validate() (in module scitex.app)":[[100,"scitex.app.validate",false]],"validate_document() (in module scitex.msword)":[[137,"scitex.msword.validate_document",false]],"verify() (scitex.repro.randomstatemanager method)":[[18,"scitex.repro.RandomStateManager.verify",false],[147,"scitex.repro.RandomStateManager.verify",false]],"verify() (scitex.rng.randomstatemanager method)":[[149,"scitex.rng.RandomStateManager.verify",false]],"verify_claim() (in module scitex.clew)":[[2,"scitex.clew.verify_claim",false],[108,"scitex.clew.verify_claim",false]],"verify_host() (in module scitex.os)":[[141,"scitex.os.verify_host",false]],"verify_notebook() (in module scitex.notebook)":[[139,"scitex.notebook.verify_notebook",false]],"verify_structure() (scitex.writer.manuscripttree method)":[[25,"scitex.writer.ManuscriptTree.verify_structure",false],[166,"scitex.writer.ManuscriptTree.verify_structure",false]],"verify_structure() (scitex.writer.revisiontree method)":[[25,"scitex.writer.RevisionTree.verify_structure",false],[166,"scitex.writer.RevisionTree.verify_structure",false]],"verify_structure() (scitex.writer.supplementarytree method)":[[25,"scitex.writer.SupplementaryTree.verify_structure",false],[166,"scitex.writer.SupplementaryTree.verify_structure",false]],"version (scitex.dev.result attribute)":[[120,"scitex.dev.Result.version",false]],"warn() (in module scitex.logging)":[[13,"scitex.logging.warn",false],[134,"scitex.logging.warn",false]],"warn_data_loss() (in module scitex.logging)":[[13,"scitex.logging.warn_data_loss",false],[134,"scitex.logging.warn_data_loss",false]],"warn_deprecated() (in module scitex.logging)":[[13,"scitex.logging.warn_deprecated",false],[134,"scitex.logging.warn_deprecated",false]],"warn_performance() (in module scitex.logging)":[[13,"scitex.logging.warn_performance",false],[134,"scitex.logging.warn_performance",false]],"warnings (scitex.writer.compilationresult attribute)":[[25,"scitex.writer.CompilationResult.warnings",false],[166,"scitex.writer.CompilationResult.warnings",false]],"watch() (scitex.writer.writer method)":[[25,"scitex.writer.Writer.watch",false],[166,"scitex.writer.Writer.watch",false]],"wordreader (class in scitex.msword)":[[137,"scitex.msword.WordReader",false]],"wordwriter (class in scitex.msword)":[[137,"scitex.msword.WordWriter",false]],"wrap_as_cli() (in module scitex.dev)":[[120,"scitex.dev.wrap_as_cli",false]],"wrap_as_mcp() (in module scitex.dev)":[[120,"scitex.dev.wrap_as_mcp",false]],"wrap_as_tracked_deletion() (in module scitex.msword)":[[137,"scitex.msword.wrap_as_tracked_deletion",false]],"wrap_as_tracked_insertion() (in module scitex.msword)":[[137,"scitex.msword.wrap_as_tracked_insertion",false]],"write() (scitex.app.filesbackend method)":[[100,"scitex.app.FilesBackend.write",false]],"write() (scitex.logging.tee method)":[[13,"scitex.logging.Tee.write",false],[134,"scitex.logging.Tee.write",false]],"write() (scitex.msword.wordwriter method)":[[137,"scitex.msword.WordWriter.write",false]],"write_file() (in module scitex.app)":[[100,"scitex.app.write_file",false]],"writer (class in scitex.writer)":[[25,"scitex.writer.Writer",false],[166,"scitex.writer.Writer",false]],"writer (scitex.config.scitexpaths property)":[[3,"scitex.config.ScitexPaths.writer",false],[112,"scitex.config.ScitexPaths.writer",false]],"yield_grids() (in module scitex.etc)":[[124,"scitex.etc.yield_grids",false]]},"objects":{"ax":[[145,0,1,"","hide_spines"],[145,0,1,"","hist"],[145,0,1,"","set_xyt"],[145,0,1,"","sns_barplot"],[145,0,1,"","sns_boxplot"],[145,0,1,"","sns_heatmap"],[145,0,1,"","sns_histplot"],[145,0,1,"","sns_jointplot"],[145,0,1,"","sns_kdeplot"],[145,0,1,"","sns_lineplot"],[145,0,1,"","sns_pairplot"],[145,0,1,"","sns_scatterplot"],[145,0,1,"","sns_stripplot"],[145,0,1,"","sns_swarmplot"],[145,0,1,"","sns_violinplot"],[145,0,1,"","stx_bar"],[145,0,1,"","stx_barh"],[145,0,1,"","stx_box"],[145,0,1,"","stx_conf_mat"],[145,0,1,"","stx_ecdf"],[145,0,1,"","stx_errorbar"],[145,0,1,"","stx_fillv"],[145,0,1,"","stx_heatmap"],[145,0,1,"","stx_image"],[145,0,1,"","stx_joyplot"],[145,0,1,"","stx_kde"],[145,0,1,"","stx_line"],[145,0,1,"","stx_mean_ci"],[145,0,1,"","stx_mean_std"],[145,0,1,"","stx_median_iqr"],[145,0,1,"","stx_raster"],[145,0,1,"","stx_rectangle"],[145,0,1,"","stx_scatter"],[145,0,1,"","stx_scatter_hist"],[145,0,1,"","stx_shaded_line"],[145,0,1,"","stx_violin"]],"scitex":[[99,1,0,"-","ai"],[100,1,0,"-","app"],[101,1,0,"-","audio"],[102,1,0,"-","audit"],[103,1,0,"-","benchmark"],[104,1,0,"-","bridge"],[105,1,0,"-","browser"],[106,1,0,"-","canvas"],[107,1,0,"-","capture"],[108,1,0,"-","clew"],[109,1,0,"-","cli"],[110,1,0,"-","cloud"],[111,1,0,"-","compat"],[112,1,0,"-","config"],[113,1,0,"-","container"],[114,1,0,"-","context"],[115,1,0,"-","cv"],[116,1,0,"-","dataset"],[117,1,0,"-","datetime"],[118,1,0,"-","db"],[119,1,0,"-","decorators"],[120,1,0,"-","dev"],[121,1,0,"-","diagram"],[122,1,0,"-","dict"],[123,1,0,"-","dsp"],[124,1,0,"-","etc"],[125,1,0,"-","events"],[126,1,0,"-","gen"],[127,1,0,"-","gists"],[128,1,0,"-","git"],[130,1,0,"-","introspect"],[131,1,0,"-","io"],[132,1,0,"-","linalg"],[134,1,0,"-","logging"],[135,1,0,"-","media"],[136,1,0,"-","module"],[137,1,0,"-","msword"],[138,1,0,"-","nn"],[139,1,0,"-","notebook"],[140,1,0,"-","notification"],[141,1,0,"-","os"],[142,1,0,"-","parallel"],[143,1,0,"-","path"],[144,1,0,"-","pd"],[17,1,0,"-","plt"],[146,1,0,"-","project"],[147,1,0,"-","repro"],[148,1,0,"-","resource"],[149,1,0,"-","rng"],[150,1,0,"-","schema"],[151,1,0,"-","scholar"],[152,1,0,"-","security"],[153,1,0,"-","session"],[154,1,0,"-","sh"],[155,1,0,"-","social"],[156,1,0,"-","stats"],[157,1,0,"-","str"],[158,1,0,"-","template"],[159,1,0,"-","tex"],[160,1,0,"-","torch"],[161,1,0,"-","tunnel"],[162,1,0,"-","types"],[163,1,0,"-","ui"],[165,1,0,"-","web"],[166,1,0,"-","writer"]],"scitex.app":[[100,2,1,"","FilesBackend"],[100,3,1,"","build_tree"],[100,3,1,"","copy_file"],[100,3,1,"","delete_file"],[100,3,1,"","file_exists"],[100,3,1,"","get_files"],[100,3,1,"","list_files"],[100,3,1,"","read_file"],[100,3,1,"","register_backend"],[100,3,1,"","rename_file"],[100,3,1,"","scaffold"],[100,3,1,"","validate"],[100,3,1,"","write_file"]],"scitex.app.FilesBackend":[[100,0,1,"","copy"],[100,0,1,"","delete"],[100,0,1,"","exists"],[100,0,1,"","list"],[100,0,1,"","read"],[100,0,1,"","rename"],[100,0,1,"","write"]],"scitex.audit":[[102,3,1,"","audit"]],"scitex.clew":[[108,3,1,"","add_claim"],[108,3,1,"","chain"],[108,3,1,"","check_stamp"],[108,3,1,"","dag"],[108,3,1,"","export_claims_json"],[108,3,1,"","hash_directory"],[108,3,1,"","hash_file"],[108,3,1,"","init_examples"],[108,3,1,"","list_claims"],[108,3,1,"","list_runs"],[108,3,1,"","list_stamps"],[108,3,1,"","mermaid"],[108,3,1,"","on_session_close"],[108,3,1,"","on_session_start"],[108,3,1,"","register_intermediate"],[108,3,1,"","rerun"],[108,3,1,"","rerun_claims"],[108,3,1,"","rerun_dag"],[108,3,1,"","run"],[108,3,1,"","stamp"],[108,3,1,"","stats"],[108,3,1,"","status"],[108,3,1,"","verify_claim"]],"scitex.cli":[[109,3,1,"","format_python_signature"],[109,3,1,"","group_to_json"],[109,3,1,"","help_recursive_to_json"],[109,3,1,"","print_help_recursive"]],"scitex.compat":[[111,3,1,"","deprecated"],[111,3,1,"","notify"],[111,3,1,"","notify_async"]],"scitex.config":[[112,2,1,"","PriorityConfig"],[112,2,1,"","ScitexConfig"],[112,2,1,"","ScitexPaths"],[112,3,1,"","get_config"],[112,3,1,"","get_paths"],[112,3,1,"","get_scitex_dir"],[112,3,1,"","load_dotenv"],[112,3,1,"","load_env_from_path"],[112,3,1,"","load_scitex_env"],[112,3,1,"","load_yaml"],[112,3,1,"","parse_src_file"]],"scitex.config.PriorityConfig":[[112,4,1,"","SENSITIVE_EXPRESSIONS"],[112,0,1,"","__init__"],[112,0,1,"","clear_log"],[112,0,1,"","get"],[112,0,1,"","print_resolutions"],[112,4,1,"","resolution_log"],[112,0,1,"","resolve"]],"scitex.config.ScitexConfig":[[112,0,1,"","__init__"],[112,5,1,"","config_path"],[112,5,1,"","flat"],[112,0,1,"","get"],[112,0,1,"","get_nested"],[112,0,1,"","print"],[112,5,1,"","raw"],[112,0,1,"","resolve"]],"scitex.config.ScitexPaths":[[112,0,1,"","__init__"],[112,5,1,"","base"],[112,5,1,"","browser"],[112,5,1,"","browser_persistent"],[112,5,1,"","browser_screenshots"],[112,5,1,"","browser_sessions"],[112,5,1,"","cache"],[112,5,1,"","capture"],[112,0,1,"","ensure_all"],[112,0,1,"","ensure_dir"],[112,5,1,"","function_cache"],[112,5,1,"","impact_factor_cache"],[112,0,1,"","list_all"],[112,5,1,"","logs"],[112,5,1,"","openathens_cache"],[112,0,1,"","resolve"],[112,5,1,"","rng"],[112,5,1,"","scholar"],[112,5,1,"","scholar_cache"],[112,5,1,"","scholar_library"],[112,5,1,"","screenshots"],[112,5,1,"","test_monitor"],[112,5,1,"","writer"]],"scitex.context":[[114,3,1,"","detect_environment"],[114,3,1,"","get_notebook_directory"],[114,3,1,"","get_notebook_info_simple"],[114,3,1,"","get_notebook_name"],[114,3,1,"","get_notebook_path"],[114,3,1,"","get_output_directory"],[114,3,1,"","is_ipython"],[114,3,1,"","is_notebook"],[114,3,1,"","is_script"],[114,3,1,"","quiet"],[114,3,1,"","suppress_output"]],"scitex.cv":[[115,3,1,"","arrow"],[115,3,1,"","blur"],[115,3,1,"","circle"],[115,3,1,"","crop"],[115,3,1,"","denoise"],[115,3,1,"","edge_detect"],[115,3,1,"","flip"],[115,3,1,"","line"],[115,3,1,"","load"],[115,3,1,"","pad"],[115,3,1,"","polylines"],[115,3,1,"","rectangle"],[115,3,1,"","resize"],[115,3,1,"","rotate"],[115,3,1,"","save"],[115,3,1,"","sharpen"],[115,3,1,"","text"],[115,3,1,"","threshold"],[115,3,1,"","to_bgr"],[115,3,1,"","to_gray"],[115,3,1,"","to_rgb"]],"scitex.dev":[[120,2,1,"","ErrorCode"],[120,2,1,"","InstallHint"],[120,2,1,"","Result"],[120,6,1,"","ScitexError"],[120,2,1,"","SideEffect"],[120,3,1,"","add_dry_run_argument"],[120,3,1,"","add_json_argument"],[120,3,1,"","async_wrap_as_mcp"],[120,3,1,"","build_docs"],[120,3,1,"","check_versions"],[120,3,1,"","classify_exception"],[120,3,1,"","dry_run_option"],[120,3,1,"","fix_mismatches"],[120,3,1,"","get_docs"],[120,3,1,"","get_ecosystem_versions"],[120,3,1,"","get_mismatches"],[120,3,1,"","handle_result"],[120,3,1,"","json_option"],[120,3,1,"","last_install_hint"],[120,3,1,"","list_versions"],[120,3,1,"","pull_local"],[120,3,1,"","remote_commit"],[120,3,1,"","remote_diff"],[120,3,1,"","result_to_mcp"],[120,3,1,"","run_as_cli"],[120,3,1,"","run_as_mcp"],[120,3,1,"","search"],[120,3,1,"","search_docs"],[120,3,1,"","supports_return_as"],[120,3,1,"","sync_all"],[120,3,1,"","sync_host"],[120,3,1,"","sync_local"],[120,3,1,"","sync_tags"],[120,3,1,"","try_import_optional"],[120,3,1,"","wrap_as_cli"],[120,3,1,"","wrap_as_mcp"]],"scitex.dev.ErrorCode":[[120,5,1,"","exit_code"]],"scitex.dev.Result":[[120,0,1,"","__post_init__"],[120,4,1,"","context"],[120,4,1,"","data"],[120,4,1,"","error"],[120,4,1,"","error_code"],[120,5,1,"","exit_code"],[120,4,1,"","hints_on_error"],[120,4,1,"","hints_on_success"],[120,4,1,"","hints_on_warning"],[120,4,1,"","idempotent"],[120,0,1,"","json_schema"],[120,4,1,"","side_effects"],[120,4,1,"","success"],[120,0,1,"","to_dict"],[120,0,1,"","to_json"],[120,4,1,"","version"]],"scitex.dev.ScitexError":[[120,0,1,"","to_dict"]],"scitex.dev.SideEffect":[[120,4,1,"","target"],[120,4,1,"","type"],[120,4,1,"","undoable"]],"scitex.etc":[[124,3,1,"","count"],[124,3,1,"","count_grids"],[124,3,1,"","search"],[124,3,1,"","yield_grids"]],"scitex.gists":[[127,3,1,"","SigMacro_processFigure_S"],[127,3,1,"","SigMacro_toBlue"],[127,3,1,"","sigmacro_process_figure_s"],[127,3,1,"","sigmacro_to_blue"]],"scitex.logging":[[134,6,1,"","AuthenticationError"],[134,6,1,"","AxisError"],[134,6,1,"","BibTeXEnrichmentError"],[134,6,1,"","ConfigFileNotFoundError"],[134,6,1,"","ConfigKeyError"],[134,6,1,"","ConfigurationError"],[134,6,1,"","DOIResolutionError"],[134,6,1,"","DTypeError"],[134,6,1,"","DataError"],[134,6,1,"","DataLossWarning"],[134,6,1,"","EnrichmentError"],[134,6,1,"","FigureNotFoundError"],[134,6,1,"","FileFormatError"],[134,6,1,"","IOError"],[134,6,1,"","InvalidPathError"],[134,6,1,"","LoadError"],[134,6,1,"","ModelError"],[134,6,1,"","NNError"],[134,6,1,"","PDFDownloadError"],[134,6,1,"","PDFExtractionError"],[134,6,1,"","PathError"],[134,6,1,"","PathNotFoundError"],[134,6,1,"","PerformanceWarning"],[134,6,1,"","PlottingError"],[134,6,1,"","SaveError"],[134,6,1,"","ScholarError"],[134,6,1,"","SciTeXDeprecationWarning"],[134,6,1,"","SciTeXError"],[134,6,1,"","SciTeXWarning"],[134,6,1,"","SearchError"],[134,6,1,"","ShapeError"],[134,6,1,"","StatsError"],[134,6,1,"","StyleWarning"],[134,2,1,"","Tee"],[134,6,1,"","TemplateError"],[134,6,1,"","TemplateViolationError"],[134,6,1,"","TestError"],[134,6,1,"","TranslatorError"],[134,6,1,"","UnitWarning"],[134,3,1,"","check_file_exists"],[134,3,1,"","check_path"],[134,3,1,"","check_shape_compatibility"],[134,3,1,"","configure"],[134,3,1,"","enable_file_logging"],[134,3,1,"","filterwarnings"],[134,3,1,"","getLogger"],[134,3,1,"","get_level"],[134,3,1,"","get_log_path"],[134,3,1,"","is_file_logging_enabled"],[134,3,1,"","log_to_file"],[134,3,1,"","resetwarnings"],[134,3,1,"","set_level"],[134,3,1,"","tee"],[134,3,1,"","warn"],[134,3,1,"","warn_data_loss"],[134,3,1,"","warn_deprecated"],[134,3,1,"","warn_performance"]],"scitex.logging.SciTeXError":[[134,0,1,"","__init__"]],"scitex.logging.Tee":[[134,5,1,"","buffer"],[134,0,1,"","close"],[134,0,1,"","fileno"],[134,0,1,"","flush"],[134,0,1,"","isatty"],[134,0,1,"","write"]],"scitex.msword":[[137,2,1,"","BaseWordProfile"],[137,2,1,"","WordReader"],[137,2,1,"","WordWriter"],[137,3,1,"","accept_all_tracked_changes"],[137,3,1,"","apply_comments_as_edits"],[137,3,1,"","clear_highlights"],[137,3,1,"","convert_docx_to_tex"],[137,3,1,"","create_post_import_hook"],[137,3,1,"","diff_docx"],[137,3,1,"","enable_track_changes"],[137,3,1,"","extract_comments"],[137,3,1,"","extract_highlights"],[137,3,1,"","extract_tracked_changes"],[137,3,1,"","get_profile"],[137,3,1,"","is_track_changes_enabled"],[137,3,1,"","link_captions_to_images"],[137,3,1,"","link_captions_to_images_by_proximity"],[137,3,1,"","list_profiles"],[137,3,1,"","load_docx"],[137,3,1,"","mark_additions"],[137,3,1,"","mark_modifications"],[137,3,1,"","normalize_section_headings"],[137,3,1,"","preserve_bold_tokens"],[137,3,1,"","register_profile"],[137,3,1,"","reject_all_tracked_changes"],[137,3,1,"","save_docx"],[137,3,1,"","summarize_diff"],[137,3,1,"","validate_document"],[137,3,1,"","wrap_as_tracked_deletion"],[137,3,1,"","wrap_as_tracked_insertion"]],"scitex.msword.BaseWordProfile":[[137,4,1,"","body_font"],[137,4,1,"","body_font_size_pt"],[137,4,1,"id3","caption_style"],[137,4,1,"id10","columns"],[137,4,1,"id1","description"],[137,4,1,"id11","double_anonymous"],[137,4,1,"id9","equation_style"],[137,4,1,"id6","figure_caption_prefixes"],[137,4,1,"","heading_background_hex"],[137,4,1,"id2","heading_styles"],[137,4,1,"","line_spacing"],[137,4,1,"id8","list_styles"],[137,4,1,"id0","name"],[137,4,1,"id4","normal_style"],[137,4,1,"","post_import_hooks"],[137,4,1,"","pre_export_hooks"],[137,4,1,"id5","reference_section_titles"],[137,4,1,"id7","table_caption_prefixes"]],"scitex.msword.WordReader":[[137,0,1,"","__init__"],[137,0,1,"","read"]],"scitex.msword.WordWriter":[[137,0,1,"","__init__"],[137,0,1,"","write"]],"scitex.notebook":[[139,2,1,"","CompiledNotebook"],[139,3,1,"","check_notebook"],[139,3,1,"","compile_notebook"],[139,3,1,"","convert_notebook"],[139,3,1,"","get_code_cells"],[139,3,1,"","get_notebook_name"],[139,3,1,"","parse_notebook"],[139,3,1,"","verify_notebook"]],"scitex.notebook.CompiledNotebook":[[139,4,1,"id2","dag"],[139,4,1,"id1","execution_order"],[139,4,1,"id0","notebook_path"],[139,4,1,"id3","runs"],[139,0,1,"","to_mermaid"],[139,0,1,"","to_script"]],"scitex.os":[[141,3,1,"","check_host"],[141,3,1,"","is_host"],[141,3,1,"","mv"],[141,3,1,"","verify_host"]],"scitex.parallel":[[142,3,1,"","run"]],"scitex.path":[[143,3,1,"","clean"],[143,3,1,"","create_relative_symlink"],[143,3,1,"","find_dir"],[143,3,1,"","find_file"],[143,3,1,"","find_git_root"],[143,3,1,"","find_latest"],[143,3,1,"","fix_broken_symlinks"],[143,3,1,"","get_data_path_from_a_package"],[143,3,1,"","get_spath"],[143,3,1,"","get_this_path"],[143,3,1,"","getsize"],[143,3,1,"","increment_version"],[143,3,1,"","is_symlink"],[143,3,1,"","list_symlinks"],[143,3,1,"","mk_spath"],[143,3,1,"","readlink"],[143,3,1,"","resolve_symlinks"],[143,3,1,"","split"],[143,3,1,"","symlink"],[143,3,1,"","this_path"],[143,3,1,"","unlink_symlink"]],"scitex.repro":[[147,2,1,"","RandomStateManager"],[147,3,1,"","fix_seeds"],[147,3,1,"","gen_ID"],[147,3,1,"","gen_id"],[147,3,1,"","gen_timestamp"],[147,3,1,"","get"],[147,3,1,"","hash_array"],[147,3,1,"","reset"],[147,3,1,"","timestamp"]],"scitex.repro.RandomStateManager":[[147,0,1,"","__call__"],[147,0,1,"","__init__"],[147,0,1,"","checkpoint"],[147,0,1,"","clear_cache"],[147,0,1,"","get_generator"],[147,0,1,"","get_np_generator"],[147,0,1,"","get_sklearn_random_state"],[147,0,1,"","get_torch_generator"],[147,0,1,"","restore"],[147,0,1,"","temporary_seed"],[147,0,1,"","verify"]],"scitex.resource":[[148,3,1,"","get_host_config"],[148,3,1,"","get_host_name"],[148,3,1,"","get_machine_config"],[148,3,1,"","get_machine_name"],[148,3,1,"","get_metrics"],[148,3,1,"","get_processor_usages"],[148,3,1,"","get_specs"],[148,3,1,"","load_config"],[148,3,1,"","log_processor_usages"],[148,3,1,"","main"]],"scitex.rng":[[149,2,1,"","RandomStateManager"],[149,3,1,"","fix_seeds"],[149,3,1,"","gen_ID"],[149,3,1,"","gen_id"],[149,3,1,"","gen_timestamp"],[149,3,1,"","get"],[149,3,1,"","hash_array"],[149,3,1,"","reset"],[149,3,1,"","timestamp"]],"scitex.rng.RandomStateManager":[[149,0,1,"","__call__"],[149,0,1,"","__init__"],[149,0,1,"","checkpoint"],[149,0,1,"","clear_cache"],[149,0,1,"","get_generator"],[149,0,1,"","get_np_generator"],[149,0,1,"","get_sklearn_random_state"],[149,0,1,"","get_torch_generator"],[149,0,1,"","restore"],[149,0,1,"","temporary_seed"],[149,0,1,"","verify"]],"scitex.security":[[152,6,1,"","GitHubSecurityError"],[152,3,1,"","check_github_alerts"],[152,3,1,"","format_alerts_report"],[152,3,1,"","get_latest_alerts_file"],[152,3,1,"","save_alerts_to_file"]],"scitex.ui":[[163,3,1,"","get_component"],[163,3,1,"","get_docs_path"],[163,3,1,"","get_static_dir"],[163,3,1,"","list_components"]],"scitex.writer":[[166,2,1,"","CompilationResult"],[166,2,1,"","ManuscriptTree"],[166,2,1,"","RevisionTree"],[166,2,1,"","SupplementaryTree"],[166,2,1,"","Writer"],[166,3,1,"","usage"]],"scitex.writer.CompilationResult":[[166,0,1,"","__str__"],[166,4,1,"","diff_pdf"],[166,4,1,"","duration"],[166,4,1,"","errors"],[166,4,1,"","exit_code"],[166,4,1,"","log_file"],[166,4,1,"","output_pdf"],[166,4,1,"","stderr"],[166,4,1,"","stdout"],[166,4,1,"","success"],[166,4,1,"","warnings"]],"scitex.writer.ManuscriptTree":[[166,0,1,"","__post_init__"],[166,4,1,"","archive"],[166,4,1,"","base"],[166,4,1,"","contents"],[166,4,1,"","git_root"],[166,4,1,"","readme"],[166,4,1,"","root"],[166,0,1,"","verify_structure"]],"scitex.writer.RevisionTree":[[166,0,1,"","__post_init__"],[166,4,1,"","archive"],[166,4,1,"","base"],[166,4,1,"","contents"],[166,4,1,"","docs"],[166,4,1,"","git_root"],[166,4,1,"","readme"],[166,4,1,"","revision"],[166,4,1,"","root"],[166,0,1,"","verify_structure"]],"scitex.writer.SupplementaryTree":[[166,0,1,"","__post_init__"],[166,4,1,"","archive"],[166,4,1,"","base"],[166,4,1,"","contents"],[166,4,1,"","git_root"],[166,4,1,"","readme"],[166,4,1,"","root"],[166,4,1,"","supplementary"],[166,4,1,"","supplementary_diff"],[166,0,1,"","verify_structure"]],"scitex.writer.Writer":[[166,0,1,"","__init__"],[166,0,1,"","compile_manuscript"],[166,0,1,"","compile_revision"],[166,0,1,"","compile_supplementary"],[166,0,1,"","delete"],[166,0,1,"","get_pdf"],[166,0,1,"","watch"]]},"objnames":{"0":["py","method","Python method"],"1":["py","module","Python module"],"2":["py","class","Python class"],"3":["py","function","Python function"],"4":["py","attribute","Python attribute"],"5":["py","property","Python property"],"6":["py","exception","Python exception"]},"objtypes":{"0":"py:method","1":"py:module","2":"py:class","3":"py:function","4":"py:attribute","5":"py:property","6":"py:exception"},"terms":{"00_abc1":[98,168,174],"00_run_al":[2,108],"00_scitex":168,"00_share":[97,166],"01_audio":168,"01_manuscript":[25,97,166],"01_python":120,"01_scholar":168,"02_supplementari":[25,97,166],"02m":[78,84,147,153],"03_interfac":120,"03_revis":[25,97,166],"05m":[18,147,149],"08_12":98,"09_error":120,"0o444":[2,108],"100ms":148,"10mb":[13,134],"12h30m45s":[18,147,149],"12h30m45s_a3bc9xy2":[18,147,149],"13d":[78,84,147,153],"14h30m15s_a3bc9xy2":[78,147],"14h30m15s_z5mr":[84,153],"167m":[82,151,169],"16g":110,"18_14":[168,174],"1d":[69,138,145],"1e":98,"1s":124,"20250531_xy9a":[18,147,149],"2025i":[18,147,149],"20260213_143022":[57,126],"20260213_143022_ab12":26,"2026i":[78,84,147,153],"250m":169,"284m":[82,151],"2d":[69,76,138,145],"2s":148,"30s":[50,119],"31d":[18,147,149],"3f":98,"8digit01":[82,151],"A":[26,28,75,76,87,97,98,100,114,120,124,131,137,144,156,158,166,168,173,174],"AND":174,"After":[2,108],"All":[3,26,27,43,60,61,65,76,87,110,112,129,130,134,137,139,156,167,168,170,173],"An":[3,112],"Both":169,"By":148,"Each":[2,26,29,82,84,108,133,137,139,151,153,158,168,169,172],"For":[3,18,29,112,120,131,147,149,168,172,174],"From":168,"How":[60,120,129],"I":[28,29,62,64,65,89,98,115,120,133,134,158,169,171,172],"IS":[2,108],"If":[2,3,13,15,18,25,26,29,100,101,102,108,112,115,120,124,134,137,139,143,147,148,149,152,166,168,172],"In":[50,76,111,119,133,168],"It":[3,26,28,60,112,120,129,137,148,168,169,171,174],"My":[57,126,137],"No":[39,84,89,108,153,158,168],"Not":[87,156,174],"On":[15,143],"Or":[52,98,121,173],"Other":137,"Out":137,"Same":[78,147],"Should":[64,133],"The":[2,3,13,15,17,28,29,57,64,65,76,84,100,108,109,110,111,112,114,120,124,126,131,133,134,137,139,143,145,148,153,163,168,169,172,173,174],"Then":173,"These":[100,169],"They":26,"This":[3,13,15,18,26,65,100,111,112,114,124,134,137,143,147,148,149,163,168,169,174],"To":[29,140,172],"What":[120,168,171],"When":[2,26,62,76,84,100,108,148,153,168,169,174],"Which":102,"With":[3,25,112,140,166],"You":[168,174],"Your":[84,153],"_":[3,18,75,112,144,147,149],"__all__":[61,130],"__call__":[18,147,149],"__file__":[15,143],"__getattr__":148,"__init__":[3,13,18,25,112,134,137,147,149,166,168],"__main__":[84,140,153,168,174],"__name__":[13,64,65,84,133,134,140,153,168,174],"__post_init__":[25,120,166],"__str__":[25,166],"__version__":[29,172],"_app":100,"_build":120,"_core":[2,108],"_db":[2,108],"_default_db_path":[2,108],"_discover_fn":120,"_doc":163,"_ecosystem":[3,112],"_figur":137,"_find_examples_dir":[2,108],"_get_one_fn":120,"_linter_plugin":133,"_out":131,"_root_fn":120,"_rule":133,"_skill":[3,112],"_sphinx_fn":120,"_sphinx_html":120,"_v":[15,143],"_warn":[13,134],"a1b2":[2,108],"a1b2c3d4e5f6":[2,108],"ab":[54,123],"abc":[13,134],"abl":137,"abov":[29,171,172],"absolut":[2,15,64,108,133,143,163],"abstract":[26,82,97,98,100,151,166,174],"academ":[89,137,158],"acc":[30,99],"accept":[3,87,112,120,137,156,173],"accept_all_tracked_chang":[129,137],"access":[15,26,29,43,60,82,84,100,112,120,129,131,143,148,151,153,168,169,172,173],"accident":[2,108],"accord":[13,134],"accumul":168,"accuraci":[30,99,101,110,120,140],"achiev":140,"acorr":76,"across":[30,64,78,82,98,99,102,116,120,133,140,147,151,167,168,171,173],"act":169,"action":[13,134],"activ":[84,100,153],"actual":[111,137,139,148],"actual_dtyp":[13,134],"actual_format":[13,134],"actual_shap":[13,134],"acute_n_sig_pathway":[2,108],"adaboost":[30,99],"adapt":[69,138],"adaptive_gaussian":115,"adaptive_mean":115,"add":[25,76,82,97,98,120,131,137,142,151,163,166,173],"add_abstract":98,"add_bibentri":[97,166],"add_claim":[2,108],"add_dry_run_argu":120,"add_edg":[52,121],"add_figur":[97,166],"add_json_argu":120,"add_nod":[52,121],"add_tabl":[97,166],"addit":[2,13,108,120,134,148,174],"adjac":139,"affect":[120,148],"afterward":139,"agent":[2,25,29,64,108,120,133,137,158,166,168,171,172,173],"agg":[84,153],"aggreg":[120,133,168,174],"agpl":28,"ahead":120,"ai":[0,25,29,60,64,120,129,133,158,166,168,171,172,173],"ai2":[25,166],"alert":[101,102,111,140,152],"alert_async":111,"algorithm":[2,108],"alia":[18,147,148,149],"alias":[69,111,138],"align":145,"align_bin":145,"allow":137,"alongsid":[131,168,171,174],"alpha":[54,69,76,98,115,123,138,145],"alphabet":26,"alphanumer":[18,147,149],"alreadi":[84,100,137,153],"also":[26,29,76,87,120,131,137,156,158,172,174],"altern":115,"alway":[13,100,120,134,148],"amplitud":[54,69,76,98,123,138,174],"analysi":[26,29,39,52,54,65,69,89,101,108,121,123,134,138,158,171,172],"analyt":[54,69,123,138],"analyz":[52,84,89,98,120,121,153,158,173],"ancestor":[2,108],"anchor":[120,137],"anchor_text":137,"angl":[54,115,123],"angle_spectrum":76,"ani":[2,3,13,18,25,26,28,62,75,76,84,100,108,112,120,131,134,137,139,140,142,144,147,148,149,153,163,166,168,171,173,174],"annot":[60,61,130,145],"anonym":137,"anova":[87,156],"anthrop":[29,172],"anti":[69,138],"anywher":28,"apa":171,"api":[25,29,31,32,33,34,35,36,37,38,40,41,42,44,45,46,47,48,49,51,53,55,56,58,59,60,63,66,67,68,70,71,72,73,74,77,79,80,81,83,85,86,88,90,91,92,93,94,95,96,109,111,148,149,163,167,168,171,172,174],"app":[28,29,60,120,129,158,167,169,171,172],"app_copy_fil":100,"app_delete_fil":100,"app_developer_guid":163,"app_dir":100,"app_file_exist":100,"app_list_fil":100,"app_read_fil":100,"app_rename_fil":100,"app_scaffold":100,"app_valid":100,"app_write_fil":100,"appdirectoriesfind":163,"appear":[120,137],"append":[13,18,84,100,134,147,149,153],"appl":124,"apple_juic":124,"appli":[25,50,115,119,137,166],"applic":[69,120,129,137,138,158],"apply_comments_as_edit":[129,137],"approach":120,"appropri":[114,131,167],"apptain":158,"arang":[54,123],"arbitrari":174,"architectur":[28,60,98,129,171],"archiv":[25,166],"area":[28,115,145,171],"arg":[84,98,100,111,124,145,153,173],"argpars":[84,120,153,168],"args_list":142,"argument":[2,18,43,84,108,112,120,142,147,148,149,153,168,174],"ariadn":[39,108],"around":[15,26,143],"arr":[62,131,174],"arr_2d":145,"arr_load":131,"array":[18,26,50,78,115,119,124,131,145,147,149,168,174],"array_data":[18,78,147,149],"arrow":[52,115,121,129],"articl":[97,166],"artifact":[2,108],"arxiv":[25,82,151,166],"as_bool":124,"as_json":120,"asian":137,"assert":[2,108,124],"asset":163,"assign":137,"assist":[28,171],"associ":139,"ast":[61,64,120,130,133,169],"asta":[25,166],"astral":[29,172],"async":[50,111,119,120],"async_wrap_as_mcp":120,"attempt":[13,134],"attent":[60,129,174],"attr":120,"attribut":[2,108,120],"auc":[30,99],"audibl":101,"audio":[28,29,60,98,129,131,168,169,171,172,173,174],"audit":[28,60,100,129,171],"augment":[18,69,138,147,149],"auth":[3,82,112,151],"auth_check":152,"authent":[3,13,82,112,134,151,152],"authenticationerror":[13,134],"author":[3,82,112,137,151],"auto":[0,2,15,18,25,28,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,100,108,120,129,131,137,143,147,149,153,166,174],"auto_uppercas":[3,112],"autom":[29,98,107,127,168,171,172,173],"automat":[2,3,18,26,27,39,62,65,76,78,84,100,101,108,112,131,133,134,140,147,149,153,167,168,170,171,174],"autonom":[98,173],"avail":[29,60,64,89,100,101,102,109,110,116,120,129,133,140,145,148,158,167,168,171,172,174],"available_key":[13,134],"avoid":[2,108],"ax":[27,28,62,76,98,131,145,170,174],"axe":[13,17,26,27,62,76,133,134,170,174],"axes_height_mm":76,"axes_hint":133,"axes_width_mm":76,"axi":[13,60,69,129,134,138],"axis1":[69,138],"axis2":[69,138],"axis_info":[13,134],"axiserror":[13,65,134],"axiswisedropout":[69,138],"axiswrapp":76,"b":[26,75,76,87,120,124,137,144,156,174],"back":[2,3,39,50,108,112,119,120,137,148,168,174],"backend":[2,26,60,84,100,108,120,129,153,168,169],"background":148,"backup":[13,134],"backup_count":[13,134],"backward":[3,18,111,112,120,147,149],"balanc":[30,99],"banana":124,"band":[54,69,123,138,173],"band_pow":[54,123],"bandit":102,"bandpass":[54,69,123,138],"bandpassfilt":[69,138],"bandstopfilt":[69,138],"bar":[30,50,76,99,119,142],"barb":76,"bare":[64,133],"barh":76,"base":[2,3,13,15,18,25,26,29,39,43,50,60,61,64,65,69,82,84,100,108,112,114,115,119,120,130,133,134,137,138,139,143,148,149,151,152,153,166,168,169,171,172],"base_dir":[3,43,112],"basewordprofil":[129,137],"bash":[3,112],"bashrc":168,"basic":[18,60,82,129,137,147,149,151,174],"batch":[60,69,129,138],"batch_fn":[50,119],"batch_numpy_fn":[50,119],"batch_pandas_fn":[50,119],"batch_siz":[50,84,119,153,168],"batch_torch_fn":[50,119],"bci":116,"becaus":[28,29,172],"becom":[2,26,84,108,139,153,168],"befor":[13,15,87,120,134,137,143,156],"before_train":[78,147],"behavior":[3,13,15,89,109,112,134,143,158,168],"behaviour":100,"behind":168,"belong":[2,108],"benchmark":[28,60,129,171],"best":[28,60,64,129,133],"beta":[54,69,123,138],"beyond":131,"bgr":115,"bgra":115,"bib":[25,82,97,98,131,151,166,174],"bibliographi":[97,131,166],"bibtex":[13,29,82,89,97,98,134,151,158,166,167,171,172,174],"bibtex_fil":[13,134],"bibtexenrichmenterror":[13,134],"bid":116,"big_fil":131,"bilater":115,"bin":145,"binari":[100,115],"binary_inv":115,"biomed":[82,116,151],"block":[137,148],"blue":[26,52,121],"blur":[115,129],"bnet":[69,138],"bnet_r":[69,138],"bodi":[137,168],"body_font":137,"body_font_size_pt":137,"boilerpl":168,"bold":137,"bonferroni":174,"bool":[2,3,13,15,18,25,84,100,108,112,114,115,120,124,134,137,143,147,148,149,152,153,166],"boolean":124,"boost":137,"boot":148,"boot_tim":148,"bot":140,"bottom":[52,115,121,145],"bound":[124,145],"boundari":[120,137],"box":[52,115,121,145],"boxplot":[76,145],"bpf":[69,138],"bracket":[13,76,134],"brain":116,"branch":[25,166],"brand":[25,166],"break":[30,64,99,133],"breakdown":[82,151],"bridg":[28,60,100,129,158,167,171],"broken":[15,18,143,147,149],"brows":116,"browser":[3,28,29,60,112,129,171,172],"browser_persist":[3,112],"browser_screenshot":[3,112],"browser_sess":[3,112],"brunner":[87,156],"bucket":137,"buffer":[13,134],"bug":168,"build":[2,29,39,97,100,108,116,120,131,139,163,166,168,172],"build_doc":120,"build_tre":100,"builder":120,"built":[120,124,131,163,167,168,171],"builtin":124,"bulk":129,"bulk_renam":120,"bullet":137,"bundl":[2,108,163,168],"by_color":137,"bypass":[84,153],"byte":[15,100,143],"c":[76,137,174],"c3d4":[2,108],"cach":[3,18,39,43,60,78,82,108,112,129,139,147,148,149,151],"cache_dir":[3,112],"cache_disk":[50,119],"cache_disk_async":[50,119],"cache_mem":[50,119],"cache_writ":120,"calc_":[30,99],"calc_bacc":[30,99],"calc_conf_mat":[30,99],"calc_feature_import":[30,99],"calc_mcc":[30,99],"calc_pre_rec_auc":[30,99],"calc_roc_auc":[30,99],"calcium":116,"calcul":174,"calculate_metr":[30,99],"call":[2,3,15,18,25,26,61,76,84,108,112,120,130,131,133,139,142,143,145,147,149,153,166,168,169,171,173],"call_rul":133,"callabl":[2,18,25,100,108,120,124,137,142,147,149,152,166],"callback":[25,120,166],"caller":[2,3,13,15,100,108,112,120,134,143],"can":[2,3,13,39,76,100,108,112,120,124,134,137,139,148,168,169,171,173,174],"cancel":110,"candid":124,"canni":115,"canon":[2,3,108,112,120,148],"canva":[28,60,98,129,171,173],"capabl":[29,98,171,172],"caption":[97,137,166],"caption_styl":137,"captur":[3,13,15,26,28,29,43,60,84,98,112,129,134,137,143,153,168,171,172,173,174],"capture_print":[13,134],"cascad":[3,112,148],"cascadeconfig":[3,112],"case":[3,112,120,129,137],"case_sensit":137,"catch":[39,108],"categor":[2,28,61,76,87,108,130,156,171],"categori":[13,60,76,98,120,124,129,131,171],"cbar_label":145,"cd":[29,172],"cell":139,"cell_id":139,"cell_index":[2,108],"cell_typ":139,"center":115,"central":[3,43,78,82,112,147,151],"cert":[3,112],"certain":148,"cfg":131,"chain":[2,60,82,120,129,151,173],"chainabl":137,"chang":[15,18,25,39,78,97,108,120,127,137,143,147,149,166,168],"channel":[60,115,129],"channelgainchang":[69,138],"char":[78,147],"charact":[2,18,84,108,147,149,153],"chat":135,"check":[2,13,15,26,29,39,43,64,87,98,100,102,108,110,112,114,120,133,134,137,139,140,141,143,152,156,169,172,173],"check_dep":100,"check_fil":[64,133],"check_file_exist":[13,134],"check_gh_auth":152,"check_github_alert":[129,152],"check_host":[129,141],"check_notebook":[129,139],"check_path":[13,134],"check_shape_compat":[13,134],"check_sourc":[64,133],"check_stamp":[2,108],"check_vers":120,"checker":[120,133],"checkpoint":[18,78,147,149],"chi":[87,156],"child":[25,26,89,158,166],"children":[100,137],"chmod":[2,108],"choic":168,"choos":171,"chronic_r2_min_pv":[2,108],"chronic_r2_n_sig_pathway":[2,108],"chunk":[2,108],"chunk_siz":[2,108],"ci":[28,98,158],"ci_low":174,"ci_upp":174,"circl":[52,115,121,129],"citat":[82,98,151,171],"citation_count":[82,151],"claim":[2,108,120,131],"claim_id":[2,108],"claim_id_or_loc":[2,108],"claim_typ":[2,108],"claim_valu":[2,108],"clariti":[18,120,147,149],"class":[3,13,18,25,60,61,65,78,100,120,129,130,133,134,137,139,145,147,149],"classif":[60,129],"classifi":[30,69,99,120,135,138],"classificationreport":[30,99],"classify_except":120,"classmethod":120,"claud":[98,173],"clean":[15,98,129,143],"cleanup":[2,108,171],"clear":[3,18,112,137,147,148,149],"clear_cach":[18,147,149],"clear_highlight":[129,137],"clear_load_cach":131,"clear_log":[3,112],"clew":[0,28,60,120,129,131,139,169,171],"clew_chain":[2,108],"clew_dag":[2,108],"clf":[30,99],"cli":[25,26,28,60,64,84,129,133,148,152,153,168,171],"cli_cmd":120,"click":[109,120,148],"client":173,"clinic":116,"clipboard":[57,126],"clobber":120,"clockwis":115,"clone":[25,29,89,97,100,120,158,166,167,172],"clone_templ":[89,158],"close":[2,13,39,57,108,115,126,134],"closer":[3,112],"closest":[3,112],"cloud":[28,29,60,100,109,129,169,171,172],"cloudfilesbackend":100,"cluster":[30,99,120],"cm":[27,170],"cmap":145,"cnn":[69,138],"cochran":[87,156],"code":[2,25,26,50,60,64,84,98,108,109,119,120,129,133,139,148,153,166,167,168,169,173,174],"code_scan":152,"code_scanning_fn":152,"codebas":120,"codeblock":[52,121],"codifi":[15,143],"coeff":[54,123],"coeffici":[30,99],"coerc":[2,108],"cohen":[87,156],"cohens_d":[87,156,174],"coher":76,"col":[75,144],"col1":[87,156],"col2":[87,156],"cold":148,"collabor":110,"collect":[2,82,108,110,148,151],"color":[84,97,98,109,115,127,137,145,153,166,168,174],"color_nam":137,"column":[52,75,87,121,137,144,145,148,156,174],"com":[29,43,112,140,172],"combin":[2,18,29,39,76,98,108,120,124,147,149,172],"command":[25,29,57,60,84,109,120,126,129,153,158,166,167,168,172,173,174],"comment":[137,139,148],"commit":120,"common":[3,75,112,137,139,144,158,174],"communiti":28,"compar":[2,39,108,137],"comparison":[39,52,60,108,121,129,133,171,174],"compat":[3,13,18,28,60,112,120,129,134,147,149,171,173],"compil":[25,29,60,98,129,139,168,169,171,172,173],"compilationresult":[25,97,166],"compile_manuscript":[25,97,166],"compile_notebook":[129,139],"compile_revis":[25,97,166],"compile_supplementari":[25,97,166],"compilednotebook":[129,139],"complet":[26,28,61,65,69,84,101,130,134,138,140,148,153,158,174],"complex":[115,137,174],"compon":[25,28,163,166],"compos":[76,169],"composit":[60,98,137],"compress":115,"compris":120,"comput":[2,18,29,39,108,110,137,147,149,172],"concept":[28,57,98,126,139,171,174],"concurr":120,"condit":[75,144],"conduct":173,"conf":131,"conf_mat_2d":145,"confid":[87,145,156,174],"config":[0,2,26,28,60,62,65,89,98,100,101,107,108,120,129,134,148,158,168,171,174],"config_dict":[3,112],"config_dir":62,"config_path":[3,112],"configfilenotfounderror":[13,65,134],"configkeyerror":[13,65,134],"configur":[3,13,25,28,60,65,82,84,98,100,120,129,131,134,137,140,148,151,153,166,171],"configurationerror":[13,65,134],"configure_cach":131,"confirm":120,"confus":[30,99,145],"connect":110,"consid":[3,18,112,124,147,149],"consist":[84,120,137,153,174],"consol":[13,65,114,134],"constant":[13,115,120,134],"consum":[2,108,120],"consumpt":120,"contain":[3,13,15,28,29,60,82,84,100,112,114,129,134,137,139,141,142,143,148,151,152,153,163,167,168,169,171,172,174],"container":[89,158],"content":[25,100,120,137,139,166],"context":[13,18,28,60,109,120,129,134,147,149,168,171,173],"continu":[54,65,69,87,123,134,138,156],"contour":[28,76,171],"contourf":76,"contract":148,"contribut":133,"contributor":120,"control":[13,76,87,134,137,156,158,173],"conveni":[3,15,112,131,137,143],"convent":[3,13,64,65,112,120,133,134],"convers":[3,60,97,112,115,127,129,137,139,166],"convert":[25,50,75,97,101,115,119,120,137,139,144,166,167,168,174],"convert_docx_to_tex":[129,137],"convert_figur":[97,166],"convert_notebook":[129,139],"coordin":[115,120,145],"copi":[2,57,97,100,108,120,126,166],"copy_fil":100,"core":[28,57,98,126,131,171,174],"corner":115,"coro_fn":120,"coroutin":120,"correct":[60,64,129,133,171,174],"correct_pvalu":[87,156,174],"correl":[30,87,98,99,156],"correspond":[137,148,174],"count":[82,98,124,129,137,151],"count_grid":[124,129],"counter":[115,124],"coupl":[54,69,123,138],"cover":[26,173,174],"cpu":148,"cpus":120,"creat":[2,3,13,15,18,25,52,62,76,78,84,97,98,108,110,112,120,121,129,131,134,137,143,147,149,152,153,158,166,168,171,173,174],"create_post_import_hook":[129,137],"create_relative_symlink":[15,129,143],"create_symlink":152,"creation":[15,26,98,129,131,143,168,173],"credenti":[3,112,168],"criteria":[82,151],"critic":[65,134],"crop":[54,115,123,129],"cross":[26,30,99,120],"crossref":[29,82,151,169,171,172],"crossref_email":[43,112],"crossvalidationexperi":[30,99],"crud":110,"csd":76,"css":163,"csv":[2,18,26,27,28,39,62,76,84,87,97,98,100,108,114,120,131,145,147,148,149,153,156,166,168,169,170,171,174],"csv_content":[97,166],"csv_text":100,"csv_to_latex":[25,97,166],"ctrl":[75,144],"ctx":109,"cubic":115,"cuda":[78,147],"cumul":145,"curl":[29,172],"current":[2,3,13,15,18,84,100,101,102,108,112,114,120,134,137,141,143,147,148,149,152,153],"current_root_hash":[2,108],"cursor":173,"curv":[30,76,99,145],"custom":[3,13,65,82,100,112,131,134,137,151,173],"cutoff":[54,123],"cutoffs_hz":[69,138],"cv":[28,29,60,129,171,172],"cv2":115,"cwd":[2,3,100,108,112],"cycl":168,"d":[18,52,62,87,121,147,149,156,168,174],"dag":[2,26,39,108,139],"dagverif":[2,108],"dandi":[29,116,169,172],"dandiset":116,"dandiset_id":116,"dark":[52,97,100,121,166],"dark_mod":[97,166],"dashboard":120,"data":[2,3,13,15,17,18,26,27,28,39,50,52,61,62,65,75,76,78,84,89,97,98,100,108,112,114,116,119,120,121,130,131,134,137,143,144,145,147,148,149,153,158,166,167,168,169,170,171,173,174],"data_dir":26,"data_gen":[78,147],"data_path":[84,153],"databas":[2,26,29,39,62,108,129,169,171,172],"dataclass":[43,112],"dataerror":[13,65,134],"datafram":[18,26,61,62,75,82,87,98,130,131,144,145,147,148,149,151,156,168,174],"datalosswarn":[13,65,134],"dataset":[28,29,60,65,98,129,131,134,168,169,171,172,173],"dataset_id":116,"date":137,"datetim":[18,28,60,129,147,148,149,171],"db":[0,2,28,29,60,76,108,116,129,139,171,172],"db_build":116,"db_dir":[2,108],"db_search":116,"db_stat":116,"dd":[13,18,84,134,147,149,153],"debug":[3,13,65,112,134],"decis":[52,121],"declar":[76,120,148],"decomposit":[54,123],"decor":[0,26,28,29,60,65,111,120,129,133,134,171,172],"deep":[82,151,174],"def":[3,26,28,39,50,62,84,98,101,107,108,112,119,120,131,133,140,142,153,168,174],"default":[2,3,13,15,18,25,26,43,52,76,84,87,89,100,101,102,108,112,115,120,121,124,134,137,139,140,143,145,147,148,149,152,153,156,158,166,168],"default_format":100,"defin":[133,173],"definit":[43,100,112,158,167],"degre":115,"del":137,"delay":124,"deleg":[2,3,28,100,108,111,112,120,140,171,174],"delet":[25,100,137,166],"delete_fil":100,"deliber":168,"delta":[54,123,137],"deltext":137,"demand":[2,108],"demo":[54,98,123],"demo_sig":[54,123],"demograph":[97,166],"denois":[115,129],"densiti":[54,69,123,138,145],"dep":102,"depend":[2,26,29,60,61,100,120,129,130,139,148,168,172],"dependabot":152,"dependabot_fn":152,"dependent_session_id":139,"deploy":168,"deprec":[13,18,50,57,65,111,119,120,126,127,129,134,147,148,149],"deprecationwarn":148,"depth":[100,137],"deriv":[2,3,43,108,112],"desc":142,"descend":120,"describ":[87,120,156],"descript":[2,29,60,65,84,89,98,100,108,116,120,129,134,137,140,142,153,158,169,172,173],"deserv":28,"design":[98,120,168,174],"desktop":140,"dest":[2,108],"dest_path":100,"destin":[2,15,108,143],"detail":[2,13,65,87,98,108,120,134,137,148,156,174],"detect":[15,18,54,100,114,115,120,123,131,135,137,143,147,149],"detect_environ":[114,129],"detect_rippl":[54,123],"detector":115,"determin":[2,26,62,108,131,168],"determinist":[18,78,147,149],"dev":[28,29,60,98,129,163,169,171,172,173],"devconfig":120,"develop":[28,120,167,169,171],"deviat":145,"df":[61,75,87,98,130,131,144,148,156,174],"df_load":131,"di":[2,108],"diagnost":[65,134],"diagram":[0,2,26,28,29,60,98,108,129,139,171,172,173,174],"diamond":[52,121],"dict":[0,2,3,13,15,18,26,28,43,60,62,84,87,100,102,108,112,120,124,129,131,134,137,139,143,147,148,149,152,153,156,168,171],"dictionari":[3,15,84,112,120,133,143,148,152,153,174],"didn":[3,112],"diff":[25,120,137,166],"diff_docx":[129,137],"diff_pdf":[25,166],"diff_stat":120,"differ":[18,39,108,137,147,149],"differenti":[69,138],"differentiablebandpassfilt":[69,138],"difflib":120,"digit":[29,54,123,172],"dim":[69,138],"dimens":[69,76,115,138],"dir":[2,3,15,26,61,84,100,108,112,130,143,153],"direct":[3,18,30,43,64,76,84,99,111,112,115,120,133,137,147,149,153],"direct_v":[3,112],"directori":[2,3,13,15,25,26,43,57,60,100,102,108,112,114,120,126,129,131,134,137,143,152,158,163,166,168,171,174],"dirnam":[15,143],"dirti":120,"disabl":[13,25,134,166],"discov":[120,133,168],"discover":163,"discoveri":[29,172,174],"discuss":[97,166],"disk":[50,119,120,131,148],"dispatch":26,"display":[107,120,135,145],"distant":[3,112],"distinct":[3,112],"distribut":[28,76,89,120,158,171],"diverg":[65,120,134],"divis":[13,134],"django":[109,120,163],"doc":[25,28,89,120,137,158,163,166],"doc_typ":[25,166],"docker":120,"docstr":[26,50,61,64,119,120,130,133,137,148,171],"document":[0,17,25,26,60,98,107,127,129,137,158,163,168],"documentsect":[25,166],"docx":137,"doe":[2,15,28,100,108,143,168],"doesn":[13,15,25,134,143,166],"doi":[13,82,98,134,151,167,173,174],"doiresolutionerror":[13,65,134],"domain":168,"don":[15,124,137,143],"done":[98,101],"dot":[3,15,52,84,100,112,121,143,153],"dotdict":[84,131,153],"dotenv":[3,112],"dotenv_path":[3,112],"dotted_path":[61,130],"dotx":137,"doubl":[52,121,137],"double_anonym":137,"download":[13,28,82,98,110,116,134,151,167,168,171,173,174],"download_pdf":[82,151],"downstream":133,"dpi":[97,166],"draft":[52,97,121,166],"draft_text":[97,166],"draw":[115,145],"dri":120,"driven":173,"drop":[69,138],"dropout":[26,69,138],"dropout_prob":[69,138],"dropoutchannel":[69,138],"dry_run":120,"dry_run_opt":120,"ds000117":116,"dsp":[0,28,29,60,129,171,172],"dst":[15,143],"dtypeerror":[13,65,134],"due":[15,143],"dump":[2,108,168],"dunder":[61,130],"duplex":115,"duplic":137,"durat":[25,54,123,148,166],"dure":[39,108,120],"e":[2,3,13,15,18,29,84,100,101,108,112,120,134,137,143,147,149,153,163,172],"e001":120,"earli":[3,112],"earlystop":[30,99],"easili":137,"east":137,"ecdf":76,"ecg":116,"ecosystem":[3,110,112,129,163,167,171,173],"ecosystem_commit":120,"ecosystem_sync":120,"edg":[52,115,121,145],"edge_detect":[115,129],"edit":[2,29,97,108,120,137,163,166,172],"edu":140,"educ":[13,134],"eeg":[69,76,82,116,138,151,174],"effect":[28,29,87,98,120,156,168,171,172,174],"effect_s":[87,98,156,174],"either":[2,108],"elaps":120,"electrophysiolog":116,"element":137,"elit":[82,151],"els":124,"elsewher":[3,112],"email":140,"email_from":140,"email_to":140,"emb":[3,112,135],"embed":[98,137,174],"emit":[2,13,108,111,134,148],"emphas":137,"emphasi":[60,69,129,138],"empir":145,"empti":[100,120,137,148],"enabl":[3,13,25,26,84,110,112,120,134,137,140,153,166,168],"enable_auto_ord":[50,119],"enable_consol":[13,134],"enable_fil":[13,134],"enable_file_log":[13,134],"enable_track_chang":[129,137],"enabled_hosts_fn":120,"end":[54,115,120,123,137,145],"endpoint":120,"ends_1d":145,"enforc":[26,50,64,119,133],"enhanc":[61,130],"enrich":[13,28,82,98,134,151,167,173,174],"enrich_bibtex":[98,174],"enrich_pap":[82,151],"enrichmenterror":[13,134],"ensur":[3,26,112,148,168,174],"ensure_al":[3,112],"ensure_dir":[3,112],"ensure_on":124,"entir":[2,69,107,108,109,120,138,171],"entri":[82,84,97,133,137,151,153,158,166,168,174],"enum":120,"env":[2,3,26,43,98,100,108,112,114,168,173],"env_prefix":[3,112],"env_registri":[3,112],"env_typ":114,"envelop":120,"environ":[2,3,43,89,98,100,108,112,114,137,158,168,173],"envvar":[43,112],"epilepsi":116,"epoch":[26,30,99,168,174],"equal":137,"equat":137,"equation_styl":137,"equival":[84,100,137,139,153,169],"error":[2,13,15,25,39,64,65,84,100,108,120,133,134,140,143,145,153,166,168,171,174],"error_cod":120,"errorbar":76,"errorcod":120,"escap":[2,108],"espeak":101,"especi":148,"essenti":[89,158],"estim":145,"etc":[18,28,60,62,76,87,101,120,129,131,137,139,147,149,156,171],"event":[28,60,129,171],"eventbus":[100,158],"eventplot":76,"everi":[2,26,28,29,62,87,107,108,124,137,156,167,168,171,172,174],"everyth":[28,168,171],"evid":120,"evolut":120,"exact":[76,87,120,124,156],"exampl":[28,29,43,61,64,76,84,129,130,153,168,171,172,174],"exc":120,"exceed":[50,119],"excel":[29,172],"except":[13,60,120,129,152],"exclud":[97,120,166],"exclus":[2,25,108,166],"execut":[2,28,39,50,84,98,108,110,114,119,120,139,142,153,168,169],"execution_ord":139,"exist":[2,3,13,15,25,39,100,108,112,120,134,137,143,148,166,168],"exit":[2,25,26,84,108,109,120,153,166],"exit_cod":[2,25,108,120,166],"exit_status":[84,153],"exp":[15,75,143,144],"exp_id":[18,147,149],"expans":[3,112,137],"expect":[29,39,108,120,172],"expected_dtyp":[13,134],"expected_format":[13,134],"expected_shap":[13,134],"expensive_comput":[50,119],"experi":[18,26,29,57,78,84,87,98,101,107,110,126,131,139,140,147,149,153,156,158,168,171,172,174],"experiment":[84,153,173],"experiment_":[18,147,149],"experiment_2025":[18,147,149],"explain":168,"explicit":[2,3,13,100,108,112,134,137],"explor":[61,129,130,167],"explore_h5":131,"explore_zarr":131,"expon":[13,134],"export":[2,25,26,27,28,29,52,61,62,76,82,84,98,108,120,121,130,131,137,145,151,153,166,168,169,170,171,172,173,174],"export_claims_json":[2,108],"expos":[98,171,173],"express":124,"ext":[15,143],"extend":168,"extens":[15,26,62,100,131,139,143,168,174],"extern":[2,108,120,173],"external_api":[50,119],"extra":[28,120,169,171],"extract":[3,13,26,61,62,112,130,134,137,139],"extract_com":[129,137],"extract_highlight":[129,137],"extract_imag":137,"extract_tracked_chang":[129,137],"extractor":[69,138],"f":[18,25,26,84,97,98,100,101,114,116,120,131,147,149,152,153,166,168],"f2":[2,108],"fa":100,"face":137,"factor":[3,82,112,115,151],"factor1":[87,156],"factor2":[87,156],"factori":[25,30,99,100,120,137,139,166],"fail":[2,13,15,65,108,120,134,143,152],"failed_nod":[2,108],"failur":[2,108,120],"fake":[2,15,18,108,143,147,149],"fall":[2,3,108,112,120,148],"fallback":[101,148],"fals":[2,3,15,18,25,84,87,97,100,108,112,114,115,120,124,137,140,143,145,147,148,149,153,156,166],"famili":120,"faq":120,"fas":100,"fast":[2,39,97,108,116,148,166,168],"faster":[29,116,172],"fastest":120,"fastnl":115,"fatal":[65,134],"fdr_bh":[87,156],"feather":131,"featur":[13,30,65,69,99,129,131,134,138,168],"fed":137,"festiv":101,"fetch":[98,167,174],"fft":[69,138],"field":[2,108,120,137],"fig":[28,62,76,98,131,137,145,168,174],"fig1":[97,166],"fig_a":76,"fig_b":76,"fig_c":76,"fig_id":[13,134],"figrecip":[17,28,29,76,120,133,168,169,171,172,174],"figur":[2,13,25,26,28,29,60,84,89,97,98,108,120,129,131,134,135,137,153,158,166,168,169,171,172,173],"figure_caption_prefix":137,"figurenotfounderror":[13,65,134],"figwrapp":76,"file":[2,3,13,15,18,25,26,28,29,39,43,62,64,65,78,84,97,98,100,101,108,112,114,115,120,131,133,134,137,139,141,143,147,148,149,152,153,158,163,166,167,168,169,171,172,173],"file_count":[2,108],"file_cr":120,"file_delet":120,"file_exist":100,"file_modifi":120,"file_path":[2,13,108,134],"file_path_filt":[2,108],"fileexistserror":[15,100,137,143],"fileformaterror":[13,65,134],"filenam":[15,18,98,114,143,147,149],"fileno":[13,134],"filenotfounderror":[2,15,100,108,120,143],"filepath":[3,13,64,89,112,133,134,158],"filesbackend":100,"filesystem":[3,100,112],"filesystembackend":100,"fill":[76,115,145],"fill_between":76,"fill_betweenx":76,"filt":[54,123],"filter":[2,13,54,60,61,64,69,100,108,115,123,129,130,133,134,138],"filterwarn":[13,65,134],"final":[2,28,39,108],"final_figur":[39,108],"final_st":107,"find":[15,61,62,75,102,130,137,139,143,144],"find_dir":[15,129,143],"find_exampl":[61,130],"find_examples_dir":[2,108],"find_fil":[15,129,143],"find_git_root":[15,129,143],"find_indi":[75,144],"find_latest":[15,129,143],"find_pval":[75,144],"finer":137,"finish":[120,140],"finished_error":[26,84,153],"finished_success":[26,84,98,140,153,168,174],"first":[2,3,13,18,78,101,108,112,120,131,134,137,147,149,168],"fisher":[87,156],"fit":124,"five":171,"fix":[13,15,18,26,64,78,84,120,133,134,143,147,149,153,168],"fix_broken_symlink":[15,129,143],"fix_loc":120,"fix_mismatch":120,"fix_remot":120,"fix_se":[18,26,78,84,129,147,149,153],"flac":131,"flag":[26,120,124,148],"flake8":[64,133],"flat":[3,87,112,120,148,156],"flatten":[3,112],"flip":[115,129],"float":[3,15,25,112,115,120,137,143,148,166],"flow":[76,139],"flush":[13,131,134],"fm":[64,133],"fm001":[64,133],"fmri":116,"fmts":131,"fn":120,"fname":[15,143],"focus":[60,89,129,137,158],"folder":[15,143],"follow":[3,13,43,60,97,112,129,134,145,166,167,173],"font":[115,137],"font_nam":137,"forc":[75,144],"force_df":[75,144],"forev":124,"form":[29,172],"format":[13,18,25,28,29,60,61,64,65,75,76,82,84,97,98,109,115,116,120,127,129,130,133,134,135,137,144,147,148,149,151,152,153,166,168,169,171,172,174],"format_alerts_report":[129,152],"format_python_signatur":[109,129],"format_result":174,"formatt":[13,134],"forward_to":[50,119],"found":[2,3,13,15,101,108,112,120,124,133,134,143,152],"four":133,"fpath":[15,143],"fr":76,"fraction":115,"framework":[28,30,50,99,119],"free":[3,112],"freq":[54,69,123,138],"freq_scal":[69,138],"freqgainchang":[69,138],"frequenc":[54,69,76,123,138,148],"fresh":[2,89,108,158],"friedman":[87,156],"friend":129,"from_scratch":[2,39,108],"from_yaml":[52,121],"frontend":[100,163],"frozen":[84,153],"fs":[54,69,123,138],"full":[2,15,26,27,29,52,61,82,89,107,108,114,120,121,130,143,148,151,158,168,169,170,171,172,173,174],"fulli":[15,143,158],"func":[61,109,130,142],"function":[2,3,15,18,26,30,50,60,61,84,99,100,109,111,119,120,124,129,130,133,137,139,142,143,145,148,149,153,167,168,169,171,174],"function_cach":[3,112],"futur":[2,108,120,137],"fuzzi":120,"g":[2,3,13,15,18,84,100,101,108,112,120,134,137,143,147,149,153,163],"g1":[87,156],"g2":[87,156],"g3":[87,156],"galleri":[28,76,98,129,171],"gamma":173,"gather":148,"gaussian":[69,115,138],"gaussianfilt":[69,138],"gen":[0,18,28,60,64,129,133,147,149,171],"gen_id":[18,78,129,147,149],"gen_timestamp":[18,78,129,147,149],"genai":[30,99],"general":[3,57,65,112,120,126,134,171],"generat":[0,2,3,15,18,25,26,27,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,108,112,120,121,123,130,137,139,143,147,149,153,166,168,170,171,172,173,174],"generate_data":[18,147,149],"generic":[2,3,108,112,120,137,140],"get":[2,3,13,15,18,25,43,61,65,76,78,87,89,97,100,108,112,114,116,120,129,130,134,137,143,147,148,149,152,156,163,166,168],"get_cache_info":131,"get_call_graph":[61,130],"get_class_annot":[61,130],"get_class_hierarchi":[61,130],"get_code_cel":[129,139],"get_code_scanning_alert":152,"get_code_templ":[89,158],"get_compon":[129,163],"get_config":[3,43,112],"get_curr":100,"get_data_path_from_a_packag":[15,129,143],"get_depend":[61,130],"get_dependabot_alert":152,"get_doc":120,"get_docs_path":[129,163],"get_docstr":[61,130],"get_ecosystem_vers":120,"get_export":[61,130],"get_fil":100,"get_gener":[18,147,149],"get_host_config":[129,148],"get_host_nam":[129,148],"get_import":[61,130],"get_info":100,"get_latest_alerts_fil":[129,152],"get_level":[13,134],"get_log_path":[13,134],"get_machine_config":[129,148],"get_machine_nam":[129,148],"get_metr":[129,148],"get_mismatch":120,"get_nest":[3,43,112],"get_notebook_directori":[114,129],"get_notebook_info_simpl":[114,129],"get_notebook_nam":[114,129,139],"get_notebook_path":[114,129],"get_np_gener":[18,78,147,149],"get_optim":[30,99],"get_output_directori":[114,129],"get_path":[3,43,112],"get_pdf":[25,166],"get_plugin":133,"get_pref":100,"get_proccessor_usag":148,"get_processor_usag":[129,148],"get_profil":[129,137],"get_scitex_dir":[3,43,112],"get_secret_alert":152,"get_sklearn_random_st":[18,147,149],"get_spath":[15,129,143],"get_spec":[129,148],"get_static_dir":[129,163],"get_this_path":[15,129,143],"get_torch_gener":[18,78,147,149],"get_type_hints_detail":[61,130],"getattr":120,"getlogg":[13,65,134],"getsiz":[15,129,143],"gib":148,"gist":[28,60,129,171],"git":[2,3,15,25,28,29,39,60,108,110,112,120,129,143,166,171,172],"git_root":[25,166],"git_strategi":[25,89,158,166],"gitea":109,"github":[29,102,152,172],"githubsecurityerror":[129,152],"give":[29,172],"given":[3,102,112,120,137,141],"glob":[2,62,108],"global":[13,18,43,65,78,84,98,112,134,147,148,149,153,173],"glue":169,"go":[84,153],"goe":[13,65,134],"googl":[29,171,172],"gothic":137,"gpu":[110,148],"gpus":148,"grain":137,"grammar":137,"graph":[61,76,130],"graphviz":[29,52,98,121,172,173],"gray":[52,121],"grayscal":115,"green":[52,121],"grid":[28,124,171],"groq":[29,172],"group":[2,26,27,52,61,76,87,108,109,121,130,131,137,156,170,173,174],"group1":[98,168,174],"group2":[98,168,174],"group_a":174,"group_b":174,"group_col":[87,156],"group_nam":[87,156],"group_to_json":[109,129],"grouper":[2,108],"gt":110,"guarante":[3,112],"guard":[64,133],"gui":107,"guid":[25,97,166,174],"guidanc":120,"guidelin":[25,60,129],"h":[27,87,156,170],"h5":[26,62,110,120,131],"h5pi":[26,120],"half":145,"hand":[2,108],"handl":[2,65,97,108,120,124,131,134,135,137,166,168,171,174],"handle_result":120,"handler":[13,120,131,134],"hardcod":[64,84,133,153],"has_doi":[82,151],"has_h5_key":131,"has_load":139,"has_sav":139,"has_sess":139,"has_zarr_key":131,"hash":[2,18,26,39,62,78,82,108,131,147,149,151,169],"hash1":[18,147,149],"hash2":[18,147,149],"hash_array":[18,78,129,147,149],"hash_directori":[2,108],"hash_fil":[2,108],"hatch":[2,108],"hdf5":[26,29,62,129,171,172],"head":[69,120,137,138],"heading_background_hex":137,"heading_styl":137,"health":120,"healthcheck":120,"heartbeat":148,"heatmap":[28,30,99,171,174],"heavy_comput":[50,119],"height":[107,115,145],"hello":101,"help":[26,84,98,109,120,153,167,168,174],"help_recursive_to_json":[109,129],"helper":[3,29,60,75,112,137,144,172],"hexadecim":[2,108],"hexbin":76,"hh":[18,84,147,149,153],"hhmm":[18,147,149],"hidden":26,"hidden_s":[26,84,131,153,168],"hide":145,"hide_spin":145,"hierarch":131,"hierarchi":[3,52,60,112,121,129,168],"high":[54,69,115,123,137,138],"high_pass":[54,123],"higher":[3,112],"highest":[3,26,112],"highlight":137,"highpass":[54,123],"highpassfilt":[69,138],"hilbert":[54,69,123,138],"hint":[84,120,133,153],"hints_on_error":120,"hints_on_success":120,"hints_on_warn":120,"hist":[27,76,145,170],"hist2d":76,"hist_bin":145,"histogram":145,"histor":[15,143],"histori":[25,29,89,139,158,166,172],"hit":[131,137],"hoc":[60,129],"hoist":139,"home":[3,15,26,112,135,143],"hood":169,"hook":[2,108,137,140],"hope":173,"horizont":[76,115,145],"host":[120,141,148,168],"host_nam":120,"host_packages_fn":120,"hostconfig":120,"hostnam":[141,148],"hot":[129,148],"hour":101,"hpc":129,"html":[100,120,163],"http":[100,120,140],"https":[29,140,172],"hub":120,"hue":145,"human":[2,25,52,108,120,121,137,166,168],"hypothesi":[29,171,172],"hz":[54,76,123],"i001":133,"icon":100,"id":[2,18,52,78,82,84,108,116,120,121,131,137,139,145,147,149,151,153],"id1":[18,147,149],"id2":[18,147,149],"id_var":[75,144],"idempot":[2,108,120,137],"ident":[100,120,168],"identifi":[2,18,75,78,100,108,137,144,145,147,149],"idx":120,"iff":137,"ignor":[3,13,64,65,112,133,134,137],"ijerph":137,"imag":[29,62,76,97,115,116,131,137,145,166,168,171,172,174],"image_dir":137,"image_hash":137,"imageri":116,"img":[27,107,115,170],"impact":[3,82,112,151],"impact_factor_cach":[3,112],"implement":[13,50,111,119,134,137,148,168,173],"import":[2,3,13,15,18,25,26,27,28,29,30,39,43,50,52,54,57,60,61,62,64,65,69,75,76,78,82,84,87,89,97,98,99,100,101,102,107,108,110,111,112,115,116,119,120,121,123,126,129,130,131,133,134,135,137,138,139,140,143,144,147,148,149,151,152,153,156,158,166,168,169,170,172,174],"import_modul":120,"importerror":[15,120,143],"importlib":120,"improv":[28,30,64,99,133],"imrad":[25,97,166],"imshow":76,"includ":[15,29,98,100,120,137,143,148,172,174],"include_run_diff":137,"includegraph":137,"inclus":[3,112],"incompat":[13,134],"incorrect":[13,134],"increment":124,"increment_vers":[15,129,143],"indent":[109,120],"independ":[18,87,147,149,156,168,169,171,174],"index":[116,137,139,171],"indic":[124,137],"individu":[75,120,144,169],"infer":174,"influenc":[82,151],"info":[3,13,25,26,43,64,65,98,100,112,114,133,134,137,148,166,168,174],"inform":[13,65,120,134,137,148],"infrastructur":[26,28],"inherit":[61,65,100,130,134],"ini":131,"init":[100,148,158],"init_exampl":[2,108],"initi":[3,13,18,25,89,112,134,147,149,158,166],"inject":[2,18,26,28,60,62,98,101,107,108,124,129,140,147,149,168,174],"input":[2,13,26,39,50,52,62,76,100,108,115,119,121,124,134,137,139,145,173],"input_path":137,"ins":137,"insensit":[120,137],"insert":137,"insid":[2,26,62,108,120,137],"inspect":[15,61,130,143],"inspector":[2,108],"instal":[28,64,76,97,98,100,111,120,124,133,140,148,152,163,166,167,168,171,173],"installed_app":163,"installed_vers":120,"installhint":120,"instanc":[3,18,25,43,78,100,112,137,147,149,166],"instead":[2,13,15,18,29,64,78,84,108,111,120,124,127,133,134,143,147,148,149,153,172,173],"institut":[82,151],"int":[2,3,13,15,18,25,100,108,112,115,120,124,134,137,142,143,147,149,166],"integ":[18,147,149],"integr":[2,13,18,25,29,100,108,109,110,129,131,134,147,149,158,166,167,169,172],"intent":168,"interact":[84,100,114,120,124,153],"interfac":[25,28,100,109,129,131,148,166,167,169,173,174],"intermedi":[2,39,108,137],"internal":[3,112,120,131,137],"interpol":115,"interpret":137,"interquartil":145,"interval":[87,145,148,156,174],"interval_":148,"intervent":168,"intro":137,"introduct":[97,137,166],"introspect":[0,28,60,98,120,129,171,173,174],"invalid":[13,134],"invalidated_claim":[2,108],"invalidpatherror":[13,65,134],"invis":[52,121,168],"invok":[2,108,120,173],"io":[0,2,26,28,29,39,60,64,76,84,89,98,107,108,120,129,133,139,153,158,168,169,171,172,174],"io003":133,"ioerror":[13,65,134],"iop":137,"ipynb":139,"ipython":[60,114,129],"ipython_fake_path":[15,143],"iqr":[87,156],"is_file_logging_en":[13,134],"is_host":[129,141],"is_ipython":[114,129],"is_notebook":[114,129],"is_script":[114,129],"is_symlink":[15,129,143],"is_track_changes_en":[129,137],"is_valid":[25,166],"isatti":[13,134],"isn":148,"iso":137,"isol":[25,89,158,166],"issu":[13,52,65,82,120,121,133,134,137,151],"ital":137,"item":[39,108,167],"iter":137,"iv":116,"japanes":137,"jax":[18,29,78,147,149,172],"job":[120,140,142],"job_id":110,"joblib":[3,112,131],"journal":[82,137,151,171],"joyplot":145,"jpeg":[115,131],"jpg":[26,62,98,131],"json":[2,26,62,82,98,102,108,109,120,131,137,151,168,173,174],"json_opt":120,"json_schema":120,"jst":137,"jupyt":[114,139],"just":[28,39,100,108,114,120,137],"keep":[13,26,98,134,137,168],"kendal":[87,156],"kept":120,"kernel":[69,115,138,145],"key":[2,3,13,52,60,102,120,121,129,134,137,139,148,168],"keyboard":124,"keyerror":[100,137],"keyword":[82,120,141,151],"kind":[61,130],"knn":[30,99],"know":148,"kolmogorov":[87,156],"kruskal":[87,156],"ksize":115,"kw":[26,62],"kwarg":[2,18,100,108,111,120,145,147,149],"l1":139,"l42":[2,108],"la":[57,126],"lab":140,"label":[13,27,52,62,76,98,100,121,134,145,170],"lack":148,"lambda":[82,151],"lanczo":115,"laplacian":115,"larg":[29,65,134,172],"last":148,"last_install_hint":120,"later":[18,39,78,108,139,147,149],"latest":[2,15,108,120,143,152],"latex":[25,29,87,89,97,98,137,156,158,166,167,168,169,171,172,173],"latex_to_csv":[97,166],"latin":137,"layer":[2,26,60,108,129,137,168],"layout":[3,52,60,97,112,121,137,166,171],"lazi":[30,99,120,168,169],"lazili":148,"lead":120,"learn":[18,29,30,82,99,116,147,149,151,172,174],"learnabl":[69,138],"learning_r":[84,153,168,174],"learningcurvelogg":[30,99],"least":[3,112,137],"left":[52,115,121,137,145],"left_to_right":[52,121],"legaci":[15,78,143,147,148],"len":152,"length":[115,124],"let":[87,156,173,174],"level":[3,13,43,60,109,112,129,137,148,168],"lib":163,"librari":[3,28,78,82,112,120,131,147,148,151],"licens":[82,151],"lifecycl":[2,100,108],"light":120,"lightweight":[29,89,158,172],"like":[2,13,18,39,61,87,108,120,130,134,147,149,156],"limit":[2,39,50,108,119,120,148,174],"limit_min":148,"linalg":[28,60,129,171],"line":[2,3,25,28,29,61,76,84,108,109,112,115,129,130,153,166,167,168,171,172,174],"line_numb":[2,108],"line_spac":137,"linear":115,"link":[2,15,26,39,108,137,143],"link_captions_to_imag":[129,137],"link_captions_to_images_by_proxim":[129,137],"link_imag":137,"link_mod":137,"linkedin":169,"linspac":[28,98,174],"linter":[0,98,120,129,169,171,173],"linter_check":[64,133],"linter_check_sourc":[64,133],"linter_list_rul":[64,133],"linux":140,"list":[2,3,15,18,25,26,29,39,61,62,64,89,97,100,101,102,108,109,110,112,116,120,124,130,131,133,137,139,140,142,143,145,147,148,149,152,158,163,166,167,172,174],"list_al":[3,112],"list_api":[61,130],"list_claim":[2,108],"list_compon":[129,163],"list_figur":[97,166],"list_fil":100,"list_of_missing_items_with_path":[25,166],"list_profil":[129,137],"list_project":[82,151],"list_rul":[64,133],"list_run":[2,39,108],"list_stamp":[2,108],"list_styl":137,"list_symlink":[15,129,143],"list_vers":120,"liter":[114,120],"literatur":[26,29,82,89,98,151,158,167,168,169,171,172,173],"live":[25,166,168],"llm":[29,52,121,129,171,172],"load":[3,13,26,28,30,39,43,52,62,82,84,98,99,108,112,115,120,121,129,133,134,137,139,148,151,153,168,169,171,174],"load_bibtex":[82,151],"load_config":[62,120,129,133,148],"load_custom":131,"load_docx":[129,137],"load_dotenv":[3,43,112],"load_env_from_path":[3,112],"load_project":[82,151],"load_scitex_env":[3,112],"load_styl":76,"load_yaml":[3,43,112],"loader":[131,168],"loaderror":[13,65,134],"local":[26,27,29,61,62,82,98,100,110,120,129,130,151,168,169,170,171,172,173],"local_data":110,"local_st":[3,112],"locat":[2,3,13,15,100,108,112,134,137,143],"log":[0,2,3,25,26,28,29,43,60,84,97,98,108,112,129,131,148,152,153,166,168,171,172,174],"log_callback":[25,166],"log_fil":[13,25,65,134,166],"log_level":[3,43,112],"log_path":[13,134],"log_processor_usag":[129,148],"log_to_fil":[13,65,134],"logger":[13,26,30,62,65,84,98,99,134,153,168,174],"logic":[28,39,100,108,168,169],"logist":[30,99],"loglog":76,"long":[75,101,107,140,144],"longer":[39,108,137],"look":[3,112],"lookup":[3,112],"lookuperror":120,"loop":[2,30,99,108,124],"loss":[13,30,65,99,134],"loud":[2,108],"low":[54,115,123],"low_pass":[54,123],"lower":[97,145,166],"lowest":[3,26,112],"lowpass":[54,123],"lowpassfilt":[69,138],"lr":[26,131,168],"ls":[57,126],"lssf":[29,172],"lxml":137,"m":[13,18,134,147,149],"machin":[2,28,30,98,99,108,120,148,168,173],"maco":140,"macro":127,"made":148,"magenta":137,"magic":[64,133],"magnitud":[69,138],"magnitude_spectrum":76,"main":[26,28,39,62,82,84,97,98,101,107,108,129,133,139,140,148,151,153,158,166,168,169,174],"maintain":[18,120,147,149,171],"major":116,"makedir":[15,131,143],"makefil":158,"manag":[3,13,18,26,29,30,57,60,78,82,84,89,98,99,100,114,126,129,134,147,149,151,153,158,167,168,169,171,172,173],"manifest":120,"manipul":[60,129,137],"mann":[87,156],"mann_whitney_u":173,"manuscript":[2,25,28,29,97,98,108,137,166,167,168,169,171,172,173],"manuscriptcont":[25,166],"manuscripttre":[25,97,166],"map":[2,26,29,108,120,124,137,172],"margin":145,"mark":[50,111,119,168,174],"mark_addit":[129,137],"mark_modif":[129,137],"markdown":[52,121,135,139],"mask":[3,112,124],"master":[82,151],"mat":[26,62,131],"match":[2,3,13,15,18,39,62,78,100,108,109,112,120,124,134,137,143,147,149],"match_typ":120,"matched_str":124,"materi":[25,97,166],"math":[15,143],"matplotlib":[17,26,27,29,62,76,84,98,133,148,153,168,169,170,172,173,174],"matric":[30,99],"matrix":[27,30,76,99,145,170,174],"matshow":76,"matter":[2,108],"matthew":[30,99],"max":[97,137,166],"max_depth":[61,100,130],"max_file_s":[13,134],"max_result":120,"maximum":[2,13,25,100,108,115,120,134,166],"maxsiz":131,"maxval":115,"may":[65,120,134,137],"mcc":[30,99],"mcnemar":[87,156],"mcp":[2,3,25,28,64,100,108,109,110,112,129,133,166,167,168,171,174],"mcp_tool":120,"mcpserver":[98,173],"md":[120,131,163],"mdpi":137,"mean":[2,87,102,108,120,142,145,156,168,169,174],"measur":[87,156],"mechan":174,"media":[28,29,60,98,129,169,171,172,173],"median":[87,115,145,156],"medium":[52,121],"meg":116,"melt":[75,144],"melt_col":[75,144],"member":[61,130],"memori":[3,50,110,112,119],"merg":[25,26,62,75,84,97,131,139,144,148,153,166],"merge_bibfil":[97,166],"merge_col":[75,144],"mermaid":[2,26,29,39,52,98,108,121,139,172,173],"messag":[2,13,18,65,108,120,134,147,149],"meta":174,"metadata":[2,60,98,108,120,129,137,139,158,163,167,173,174],"method":[3,18,27,76,82,87,97,100,112,114,115,129,137,147,149,151,156,166,170,174],"metric":[60,69,129,138,148],"middl":145,"might":[120,148],"migrat":17,"millimet":76,"millimetr":171,"mimic":116,"min":[29,172],"min_cit":[82,151],"min_impact_factor":[82,151],"minim":[30,84,89,99,137,153,158],"minut":[29,107,148,171,172],"mirror":[2,100,108,120,174],"miscellan":124,"mismatch":[39,108,120],"miss":[13,39,64,100,108,120,131,133,134,137,174],"missing_ok":[15,143],"mit":[82,151],"mk_spath":[15,57,64,126,129,133,143],"ml":[29,172],"mm":[13,18,60,84,134,147,149,153],"mmd":[52,121],"mmdd":[18,147,149],"mnet1000":[69,138],"mngs":[64,133],"mock":[18,147,149],"mode":[13,29,60,84,97,111,115,129,134,139,153,166,172],"model":[13,18,28,30,50,65,78,82,84,99,119,131,134,137,147,149,151,153,168,171,173],"model_nam":[13,134],"model_path":[30,99],"model_select":[18,147,149],"modelerror":[13,65,134],"modifi":[26,28,115,120,137,148],"modul":[0,3,13,15,17,18,25,26,28,84,98,129,153,167,168,169,171,174],"modular":[169,171],"modulationindex":[69,138],"module_path":120,"monitor":[3,30,98,99,110,112,148,167,173],"monolith":168,"motor":116,"mountpoint":[100,158],"move":[26,57,84,100,126,153],"mp3":[101,131],"mro":[61,130],"ms":[137,148],"mss":[29,172],"msword":[28,60,129,171],"multi":[2,30,69,76,87,99,108,120,138,140,156,169,174],"multi_par":[2,108],"multilin":109,"multipl":[2,60,64,75,76,82,97,101,102,108,120,129,131,133,137,144,151,166,171,174],"multitaskloss":[30,99],"munzel":[87,156],"must":[64,100,115,120,133],"mutat":[2,108,120,137],"mute":[52,121],"mutual":[2,25,108,166],"mv":[76,129,141],"my_app":158,"my_contain":158,"my_data":[18,131,147,149],"my_experi":[89,158],"my_experiment_1":[57,126],"my_func":133,"my_packag":[89,133,158],"my_pap":[25,89,97,158,166,173],"my_project":[89,158],"my_research":[82,151],"my_s3_factori":100,"my_script":[64,84,133,153],"my_script_out":[84,153],"my_tool":[100,120],"myfil":[15,143],"myfile_v001":[15,143],"mymodul":[3,112],"mypi":[29,172],"myresearch":[82,151],"myrule001":133,"myrule002":133,"n":[18,64,78,84,98,115,133,137,147,149,153,168,174],"n_band":[69,138],"n_bin":[69,138],"n_blks":[69,138],"n_chs":[69,138],"n_chs_in":[69,138],"n_epoch":168,"n_fft":[69,138],"n_group":[87,156],"n_job":142,"n_layer":168,"n_out":[69,138],"n_sampl":[28,174],"n_sig":[2,108],"name":[2,3,13,15,18,25,26,61,64,78,84,87,100,102,108,109,111,112,114,120,124,130,133,134,137,139,140,143,147,148,149,153,156,158,163,166,167],"name_color":109,"namespac":[100,168,169],"nan":[15,143],"narrow":137,"nativ":140,"nature12373":[82,151],"ncol":76,"ndarray":[18,26,62,115,131,147,149],"nearest":[2,108,115,137],"necessari":[3,13,112,134,148],"need":[2,29,84,100,108,120,131,137,153,163,168,171,172],"negat":[13,120,134],"neither":120,"nest":[3,43,100,109,112],"network":[13,69,120,134,138,148],"neural":[13,69,82,134,138,151,173],"neuroimag":98,"neuron":145,"neurophysiolog":116,"neurosci":[29,69,116,138,172],"never":[3,13,112,120,131,134,168,174],"new":[3,18,25,78,97,100,111,112,120,131,137,147,149,166],"new_func":[65,134],"new_function_nam":120,"new_nam":[13,111,134],"new_path":100,"new_sourc":[15,143],"new_target":[15,143],"newli":137,"next":[15,18,28,137,143,147,149,173],"nn":[0,28,60,65,129,134,171],"nnerror":[13,65,134],"no_fig":[97,166],"no_tabl":[97,166],"node":[60,129,148],"nois":[78,98,147],"noisi":98,"non":[2,13,64,65,84,87,108,120,133,134,148,153,156],"none":[2,3,13,15,18,25,84,89,100,102,108,109,112,114,115,120,134,137,139,143,145,147,148,149,152,153,158,166],"normal":[15,28,52,87,98,120,121,137,143,156],"normal_styl":137,"normalize_head":137,"normalize_section_head":[129,137],"not_found":120,"not_impl":[50,119],"notat":[3,84,100,112,153],"notch":[54,123],"note":[3,112],"notebook":[28,60,84,114,129,153,168,171],"notebook_directori":114,"notebook_nam":114,"notebook_path":[2,108,139],"noth":[39,108],"notif":[84,98,101,111,120,129,153,169,171,173,174],"notifi":[28,60,84,111,129,140,153,169],"notify_async":[111,129],"now":[2,18,50,108,119,137,147,149],"now_fn":[18,147,149],"np":[18,28,54,64,87,98,123,131,133,147,149,156,168,174],"np_gen":[78,147],"npi":[26,62,131,168,174],"npz":[26,62,98,131],"npzfile":62,"nrow":76,"num":[82,151],"number":[2,3,13,15,18,61,64,108,112,120,124,130,133,134,137,142,143,145,147,149,174],"numpi":[18,26,28,29,50,54,62,64,78,87,98,119,123,124,131,133,147,149,156,172,174],"numpy_fn":[50,119],"nvidia":148,"nwb":116,"o":[28,29,62,64,65,89,98,115,120,133,134,137,158,169,171,172,173],"obj":[18,62,147,149,174],"object":[2,3,13,15,18,25,26,50,60,62,108,112,119,120,127,129,131,134,137,139,143,145,147,148,149,166,168,174],"observ":[87,156],"occur":140,"occurr":[13,134],"odd":115,"offici":127,"offlin":116,"often":120,"ok":120,"old":[18,111,120,147,149],"old_func":[65,134],"old_function_nam":120,"old_nam":[13,134],"old_path":100,"on_compil":[25,166],"on_progress":120,"on_session_clos":[2,108],"on_session_start":[2,108],"onc":[13,29,100,134,148,172],"one":[2,3,13,26,75,76,84,87,108,112,114,116,120,124,134,137,140,142,144,145,148,153,156,173,174],"onli":[2,3,13,29,39,84,89,108,111,112,120,124,134,137,139,148,153,158,168,172],"only_perfect_match":124,"op":137,"open":[2,64,82,108,131,133,137,151,173],"openai":[29,172],"openalex":[29,82,151,169,171,172],"openathen":[3,82,112,151,168],"openathens_cach":[3,112],"opencv":[29,172],"openneuro":[29,116,169,172],"oper":[13,57,65,89,109,120,126,134,137,152,158],"opt":[3,112],"optim":[30,50,52,99,119,121],"option":[2,3,13,15,18,25,29,60,87,100,102,108,109,112,114,115,120,124,129,134,137,143,145,147,148,149,152,156,163,167,172],"orang":[26,124],"orange_juic":124,"orchestr":[28,102,168,169,173],"order":[2,3,60,108,112,124,129,137,139],"org":140,"organiz":[26,60,129,139,168,174],"origin":[2,3,25,39,50,62,89,108,112,119,120,137,158,166],"os":[2,18,28,60,108,129,131,147,148,149,171],"oscil":[82,151,173],"oserror":[15,143],"otherwis":[3,15,100,112,143,148],"otsu":115,"outcom":[52,97,121,166],"outcome_typ":[87,156],"output":[2,13,15,25,26,39,50,57,60,62,65,82,97,98,100,101,108,109,114,115,119,120,124,126,129,131,134,137,139,143,148,151,158,166,168,171,173,174],"output_dir":[2,26,27,30,82,99,108,114,120,151,152,170],"output_directori":114,"output_fil":[97,102,166],"output_path":[76,137],"output_pdf":[25,97,166],"overal":120,"overlap":137,"overlay":135,"overleaf":[29,171,172],"overrid":[2,3,108,112,114,120,148,152,168],"overridden":[3,112],"overview":[2,26,28,98,108,129],"overwrit":[13,15,100,134,137,143],"overwritten":[2,108],"owner":152,"owning":120,"owns":[137,168],"p":[2,64,75,82,87,98,108,133,144,151,156,174],"p_to_star":[87,156],"p_valu":[76,174],"pa":[2,64,108,133],"pac":[54,69,123,138],"packag":[2,3,15,28,29,61,64,76,89,97,100,108,112,130,131,133,140,143,158,163,166,167,168,171,172],"package_nam":120,"package_str":[15,143],"pad":[115,129],"padj":[2,108],"page":[29,120,168,171,172],"pair":[87,137,156],"palett":[84,137,153,168,174],"panda":[18,26,29,50,75,82,87,119,131,144,147,149,151,156,172,174],"pandas_fn":[50,119],"pane":135,"panel":[76,127,174],"panel_label":76,"paper":[2,13,28,29,60,89,97,98,108,129,134,137,158,166,167,169,171,172,173,174],"paper_directori":89,"paper_titl":[13,134],"paragraph":137,"paragraph_idx":137,"paragraph_rang":137,"parallel":[28,29,60,120,129,171,172],"param":[26,50,119,168,174],"paramet":[2,3,13,15,18,25,26,60,61,69,98,100,102,108,109,112,114,115,120,124,129,130,134,137,138,139,142,143,145,147,148,149,152,158,166,168,173,174],"parametr":[87,156],"params_grid":124,"parent":[2,3,25,26,89,100,108,112,158,166],"parent_sess":[2,39,108],"parenthes":[13,134],"parquet":[26,62,131],"pars":[3,25,61,84,112,130,137,139,153,166,168,171,174],"parse_bibtex":174,"parse_custom":131,"parse_notebook":[129,139],"parse_src_fil":[3,112],"parser":[3,112,120],"part":[2,108],"parti":[61,130],"partial":120,"partit":148,"pass":[2,3,18,25,108,112,120,147,149,166,168,173,174],"password":[3,112],"paste":[57,126],"path":[0,2,3,13,25,26,28,29,57,60,61,64,65,84,87,97,100,102,108,114,115,120,126,129,130,133,134,137,139,148,152,153,156,163,166,168,169,171,172],"path_nam":[3,112],"path_str":[15,143],"path_with_spac":[15,143],"patherror":[13,65,134],"pathlib":[97,100,166],"pathnotfounderror":[13,65,134],"pathway":[2,108],"patienc":[30,99],"pattern":[2,3,13,15,18,57,62,89,98,108,112,124,126,133,134,139,143,147,149,158,167,171,173],"payload":137,"pca":[30,99],"pcolor":76,"pcolormesh":76,"pd":[0,28,60,87,129,131,133,139,156,168,171,174],"pdf":[3,13,25,26,29,62,82,97,98,100,112,131,134,151,166,167,168,172,173,174],"pdfdownloaderror":[13,65,134],"pdfextractionerror":[13,134],"pdflatex":[25,166],"pdfs":[82,151,171],"pearson":[87,98,156],"pep":148,"per":[2,13,26,28,69,84,108,120,134,137,138,139,145,148,153,171],"per_cel":139,"perc":145,"percentag":145,"perform":[13,65,120,134],"performancewarn":[13,65,134],"perm":[18,147,149],"permiss":[15,143,148],"permissionerror":[15,143,148],"permut":[18,69,138,147,149],"persist":[3,50,112,119,140],"perspect":168,"pet":116,"phase":[54,69,123,138],"phase_spectrum":76,"phrase":120,"physiolog":[98,116],"physionet":[29,116,169,172],"pi":[28,54,98,123,174],"pick":137,"pickl":[26,62,84,131,153,174],"pid":[84,153],"pie":76,"piec":[100,148],"pillow":[29,172],"pip":[29,64,76,97,100,102,120,133,140,163,166,167,168,169,172],"pip_project":[89,158],"pipelin":[2,26,52,69,82,108,120,121,138,151,168],"pivot":[75,144],"pkg":[3,112,120],"pkg_name":120,"pkl":[18,26,62,78,84,131,147,149,153,174],"place":[115,120,137,168],"plain":[87,115,120,137,156,168],"plateaus":[30,99],"platform":[100,109,110,148,169],"play":167,"playback":[98,101,167,173],"playwright":[29,172],"pleas":[127,137],"plot":[13,17,26,28,29,30,60,62,64,65,84,89,97,98,99,120,131,133,134,153,158,166,168,171,172,174],"plot_":[27,76,170],"plot_bar":[27,76,170],"plot_barh":[27,170],"plot_box":[27,76,170],"plot_conf_mat":[27,170],"plot_contour":[27,170],"plot_ecdf":[27,170],"plot_errorbar":[27,170],"plot_feature_import":[30,99],"plot_fill_between":[27,170],"plot_fillv":[27,170],"plot_heatmap":[27,76,170],"plot_imag":[27,170],"plot_imshow":[27,170],"plot_joyplot":[27,170],"plot_kd":[27,76,170],"plot_learning_curv":[30,99],"plot_lin":[27,28,62,76,98,170,174],"plot_mean_ci":[27,170],"plot_mean_std":[27,170],"plot_median_iqr":[27,170],"plot_pre_rec_curv":[30,99],"plot_rast":[27,170],"plot_rectangl":[27,170],"plot_roc_curv":[30,99],"plot_scatt":[27,76,98,170],"plot_shaded_lin":[27,170],"plot_violin":[27,76,170],"plottingerror":[13,65,134],"plt":[0,26,27,28,29,60,61,62,64,84,89,98,129,130,131,133,139,153,158,168,169,170,171,172,173,174],"plt_bar":173,"plugin":[64,129],"plus":[84,120,153],"pmid":[82,151],"png":[26,28,30,39,62,76,84,97,98,99,100,107,108,115,131,135,153,166,168,174],"point":[15,18,76,82,84,97,115,133,143,147,149,151,153,158,166,168,174],"poll":[110,120],"polylin":[115,129],"pool":142,"pop":120,"popmean":[87,156],"popul":[2,108,137],"port":[3,112],"portion":[18,147,149],"posit":[52,115,121,124,137,145],"posix":120,"posixpath":[2,108,137],"post":[29,60,98,129,140,169,172,173],"post_import_hook":137,"postgresql":[29,172],"posthoc_test":[87,156],"potenti":[13,15,65,134,143],"power":[29,54,69,76,98,123,138,168,171,172,174],"pprint":148,"practic":[28,60,64,129,133],"pre":[26,84,100,120,137,153],"pre_export_hook":137,"preced":[3,43,112,137],"precis":[30,76,99],"predict":[50,119,145],"pref":100,"prefer":[13,100,120,134],"prefix":[3,15,29,64,112,133,137,143,172],"preprint":[82,151],"preprocess":[52,121],"present":[2,108,137],"preserv":[25,50,89,115,119,137,158,166],"preserve_bold_token":[129,137],"preserve_doc":[50,119],"preset":[60,171],"press":148,"prevent":100,"preview":120,"primari":[52,84,121,153],"primarili":120,"primit":[3,112],"principl":[26,98],"print":[3,13,18,25,29,84,87,97,100,109,112,114,116,120,124,127,131,134,137,139,147,148,149,152,153,156,158,166,168,172,174],"print_help_recurs":[109,129],"print_resolut":[3,112],"printer":124,"prior":[39,108],"prioriti":[3,26,60,129],"priorityconfig":[3,43,112],"privat":[3,61,112,130],"prob":[69,138],"probe":148,"problem":[65,134],"proc":[52,121],"proceed":120,"process":[3,13,25,29,39,52,54,60,65,84,107,108,112,115,121,123,129,134,142,148,153,166,172],"processor":142,"processor_usag":148,"produc":[2,18,26,84,108,147,149,153,171],"product":[2,108,120,124],"profil":[3,98,112,129,168,173],"program":[65,124,134,148],"programmat":[43,110,112,171],"progress":[25,50,107,119,142,166],"progress_callback":[25,166],"proj":135,"project":[2,3,15,25,28,29,43,60,98,100,108,109,110,112,129,133,143,148,167,168,171,172,173],"project_dir":[25,166],"project_nam":[82,151],"project_root":[2,108],"prompt":[25,97,166],"proof":[2,108],"proper":[120,137,148],"properti":[3,13,112,120,134],"protect":[61,130],"protocol":[100,168,171,173],"proven":[28,39,60,84,108,131,153],"provid":[3,13,15,18,26,84,100,109,110,111,112,115,120,124,131,134,137,139,143,147,148,149,153,163,167,168,169,171,174],"proxim":137,"psd":[54,69,76,123,138],"psutil":148,"pt":[26,62],"pt1":115,"pt2":115,"pth":[26,62],"public":[2,3,26,28,29,52,61,76,82,87,98,100,108,112,116,120,121,130,148,151,156,168,169,171,172,173],"publish":[82,151],"pubm":[82,151],"pull":[29,120,148,168,172],"pull_loc":120,"pure":[2,100,108,141],"purpl":26,"purpos":120,"push":[110,120],"put":[26,84,153],"puzzl":100,"pvalu":[87,98,156],"py":[2,3,15,26,39,64,84,89,108,110,112,133,139,143,153,158,174],"pyarrow":26,"pydant":[82,151],"pypi":[89,120,158,169],"pyplot":[84,153],"pyproject":[2,64,108,120,133,158],"pytest":[29,172],"python":[3,25,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,90,91,92,93,94,95,96,97,100,102,109,110,112,120,121,129,130,131,133,139,148,153,163,166,167,168,171,172,173,174],"python3":163,"pytorch":[18,30,50,62,69,99,119,138,147,149],"pyttsx3":101,"pyyaml":[26,148],"q":[61,87,130,156],"qq":[61,130],"qualiti":[97,115,120,166,174],"queri":[2,13,100,108,110,120,134,139,167,173,174],"quick":[28,60,89,129,158],"quickstart":171,"quiet":[97,114,129,166],"quiver":[27,76,170],"r":[2,98,108,116,137],"radius":115,"rais":[2,13,15,50,100,108,119,120,134,137,143,148,152],"ram":148,"randn":[18,131,147,149,174],"random":[3,18,26,28,69,78,84,87,98,112,124,131,138,147,149,153,156,168,174],"random_char":[18,147,149],"random_st":[18,147,149],"randomstatemanag":[18,60,84,129,149,153],"rang":[30,54,99,123,137,145,174],"rank":[87,156],"raster":145,"rather":120,"rational":174,"raw":[3,28,61,98,112,130,133,158],"re":[2,28,39,108,120,168,169],"reach":[3,112],"react":28,"reactome_pathways_v2024":[2,108],"read":[2,15,25,43,100,108,112,120,131,137,143,166,168],"read_csv":[87,133,139,156],"read_fil":100,"read_on":[2,108],"readabl":[2,25,52,108,120,121,137,152,166],"reader":[137,168],"readi":[29,76,87,98,100,120,140,156,158,168,169,171,172,173],"reading_direct":[52,121],"readlink":[15,129,143],"readm":[2,25,108,166],"real":124,"reason":[13,50,119,134],"rebuild":137,"recal":[30,99],"receiv":[84,153,173],"recent":[82,120,151],"recip":[26,28,62,76,98,131],"recogn":[2,108],"recognis":137,"recommend":[3,28,60,98,112,129,167,171,174],"recommend_test":[87,156,174],"recompil":[25,166],"reconstruct":[26,139],"record":[2,26,27,39,62,76,101,107,108,110,120,131,137,139,170],"recordingfigur":168,"recoveri":120,"rectangl":[115,129,145],"recurs":[2,15,61,98,100,108,109,130,143,167,174],"red":[52,98,121,145],"redirect":[13,60,84,129,153],"redistribut":28,"ref":[82,98,135,151,174],"refer":[26,28,49,53,60,74,86,88,127,137],"referenc":[2,108],"reference_section_titl":137,"reflect":115,"regardless":[50,119],"regener":[2,108],"regex":[13,134],"region":[115,145],"regist":[2,100,108,120,131,137,163],"register_backend":100,"register_intermedi":[2,108],"register_load":131,"register_profil":[129,137],"register_sav":131,"registr":163,"registri":[2,108,120,129],"regress":[30,87,99,156],"regular":[107,124],"reject":[69,137,138],"reject_all_tracked_chang":[129,137],"relat":[2,13,15,26,100,108,120,131,134,143],"relationship":26,"releas":[25,166,168],"relev":120,"reload":129,"remain":[2,108,137,168],"remedi":120,"rememb":[131,168],"remot":[2,98,108,110,120,168,173],"remote_commit":120,"remote_diff":120,"remov":[2,15,18,25,108,111,120,137,143,147,149,166],"removal_vers":111,"renam":[100,129],"rename_fil":100,"render":[52,121,135,137],"repeat":[87,116,131,156],"rephras":137,"replac":[137,168],"replic":[115,168],"repo":[89,110,120,152,158],"repoint":[15,143],"report":[30,99,102,120,152,174],"repositori":[15,25,29,116,143,152,166,172],"repr":[2,108],"represent":137,"repro":[0,26,28,60,84,129,149,153,171],"reproduc":[2,18,26,28,29,39,60,62,64,78,84,89,98,108,133,139,147,149,153,158,168,169,171,172,173,174],"reproduct":98,"request":[84,100,120,137,153],"requir":[13,25,28,100,111,120,134,137,166,168,171],"rerun":[2,39,108],"rerun_claim":[2,108],"rerun_dag":[2,108],"research":[89,98,140,158,167,168,169,171,173],"research_minim":[89,158],"reserv":[120,137],"reset":[13,18,78,129,134,147,149],"resetwarn":[13,134],"reshap":[75,144],"residu":[69,138],"resiz":[115,129],"resna":137,"resnet1d":[69,138],"resolut":[2,3,13,60,82,87,108,120,129,131,134,148,151,156],"resolution_log":112,"resolv":[2,3,15,26,29,43,100,108,112,131,143,163,168,172],"resolve_spec":[2,108],"resolve_symlink":[15,129,143],"resort":148,"resourc":[15,28,60,110,129,143,171],"respons":[97,120,166,168,173],"rest":[116,174],"restor":[18,78,137,147,149],"result":[2,18,25,26,30,39,50,52,57,62,64,78,82,84,87,97,98,99,100,101,102,107,108,109,110,116,119,120,121,126,131,133,139,140,142,147,149,151,153,156,166,168,173,174],"result_3":[2,108],"result_to_mcp":120,"retag":137,"retri":120,"retriev":[2,108,110],"retrofit":120,"return":[2,3,13,15,18,25,26,28,39,50,52,61,62,64,76,84,87,98,100,101,102,107,108,109,112,114,115,119,120,121,124,130,131,133,134,137,139,140,142,143,145,147,148,149,152,153,156,163,166,168,173,174],"return_a":[87,98,120,156,174],"reus":[50,119],"reusabl":[115,148],"revers":[82,120,151],"review":[26,168,171],"review_pap":[82,151],"revis":[25,97,137,166],"revisioncont":[25,166],"revisiontre":[25,97,166],"rewrit":137,"rfc":[2,108],"rfc3161":[2,108],"rgb":115,"ridgelin":145,"right":[52,115,121,145],"rippl":[54,123],"risky_cal":[50,119],"rng":[3,18,28,43,60,78,112,129,147,171],"rngg":[84,153,174],"robust":[18,87,147,149,156],"roc":[30,99],"roll":[2,108],"root":[2,3,13,15,25,98,100,108,112,120,134,143,166,168,173],"root_caus":[2,108],"root_dir":[15,143],"root_path":135,"rotat":[13,115,129,134],"rough":[29,172],"round":[52,121],"routin":127,"row":145,"rst":131,"ruff":[29,172],"rule":129,"run":[2,18,25,26,28,39,57,65,84,87,100,101,102,107,108,110,114,120,124,126,129,134,137,139,140,142,147,148,149,153,156,166,167,168,171,173,174],"run_as_c":120,"run_as_mcp":120,"run_experiment_gui":107,"run_info":137,"run_test":[168,174],"runaway":100,"runnabl":[2,108,158],"runs_chang":137,"runtim":[2,28,100,108,120,133,168,169,174],"runtimeerror":120,"rust":[29,172],"s":[2,3,13,15,18,25,26,29,39,61,64,76,84,87,89,98,100,108,109,112,116,120,124,130,133,134,137,139,143,147,148,149,153,156,158,163,166,168,172,174],"s001":133,"s002":[64,133],"s01":174,"s3":100,"s41586":[173,174],"safe":[82,120,141,151,171],"safeti":120,"samp_rat":[69,138],"sampl":[87,145,148,156,174],"sample_r":[69,138],"sample_s":[87,156],"sampling_r":[69,138],"sandbox":[2,108],"save":[13,15,18,26,28,39,52,60,64,76,82,84,98,107,108,114,115,120,121,129,133,134,137,139,143,147,148,149,151,152,153,158,168,171,174],"save_alerts_to_fil":[129,152],"save_custom":131,"save_docx":[129,137],"save_papers_as_bibtex":[82,151],"save_papers_to_librari":[82,151],"save_path":[18,147,149],"save_summari":[30,99],"saveerror":[13,65,134],"savefig":168,"saver":131,"scaffold":[2,89,98,100,108,158,167,173],"scalar":[2,108],"scale":[69,115,138],"scan":[102,137],"scatter":[28,76,171],"scatter_alpha":145,"schema":[28,60,120,129,171],"scheme":[97,166],"scholar":[0,3,13,28,29,43,60,65,89,98,109,112,129,134,158,168,169,171,172,173,174],"scholar_cach":[3,112],"scholar_fetch_pap":173,"scholar_librari":[3,112],"scholar_search_pap":173,"scholarconfig":[82,151],"scholarerror":[13,65,134],"scholarlibrari":[82,151],"scienc":[2,39,108],"scientif":[15,18,28,29,64,89,98,100,116,120,131,133,137,143,147,149,158,168,169,171,172,173],"scikit":[18,29,30,99,147,149,172],"scipi":[26,29,64,133,172],"scitex":[0,26,27,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,107,108,109,110,111,112,114,115,116,119,120,121,123,124,126,127,129,130,131,133,134,137,138,139,140,141,142,143,144,147,148,149,151,152,153,156,158,163,166,167,168,169,170,172,173,174],"scitex_":[3,112],"scitex_api_token":100,"scitex_app":100,"scitex_audit":102,"scitex_auto_regist":[2,108],"scitex_clew":[2,108],"scitex_clew_claims_json":[2,108],"scitex_cloud":100,"scitex_config":[3,112],"scitex_dev":120,"scitex_dir":[3,43,112],"scitex_env_src":[3,98,112,168,173],"scitex_etc":135,"scitex_io":[168,169],"scitex_lint":[64,133],"scitex_log":[13,134],"scitex_logging_level":[43,112],"scitex_msword":137,"scitex_notebook":139,"scitex_path":[15,143],"scitex_port":[3,112],"scitex_repro":[18,147,149],"scitex_resourc":148,"scitex_secur":152,"scitex_session_id":[2,108],"scitex_stat":168,"scitex_ui":163,"scitex_writ":[25,166],"scitexconfig":[3,43,112],"scitexdeprecationwarn":[13,65,134],"scitexerror":[13,65,120,134],"scitexfileformatt":[13,134],"scitexpath":[3,43,112],"scitexwarn":[13,65,134],"scope":[3,112,120,133,148],"score":[30,75,82,87,99,120,144,151,156],"screen":[3,98,107,112,167,173],"screenshot":[3,29,98,107,112,167,172,174],"screenshots_dir":[3,112],"script":[2,3,15,25,26,39,64,84,89,98,108,110,112,114,120,133,137,139,143,153,158,166,168,174],"script_nam":131,"script_out":[26,84,98,114,153,168,174],"script_path":[2,108],"sd":[145,174],"sdir":[13,65,134],"sdir_out":[84,153],"sdir_run":[84,153],"sdir_suffix":[84,153],"sdk":[28,29,100,120,169,172],"seaborn":[29,60,129,172],"seam":[2,108,124],"search":[2,3,13,15,28,29,82,108,110,112,120,124,129,134,143,151,167,168,169,171,172,173,174],"search_doc":120,"search_librari":[82,151],"searcherror":[13,65,134],"second":[2,25,50,97,107,108,119,131,137,148,166],"secondari":[52,121],"secret":[3,112,152],"secrets_fn":152,"section":[82,97,127,137,151,166],"secur":[28,60,102,129,171],"security_dir":152,"see":[2,17,26,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,100,108,120,126,137,148,167,172,174],"seed":[18,26,78,84,147,149,153,168,174],"select":[64,127,133],"self":[3,26,29,84,100,112,153,172,174],"semant":[29,52,82,121,137,151,172],"semilog":76,"semilogx":76,"sen":[87,156],"send":[84,153],"sensit":[3,112],"sensitive_express":[3,112],"sentinel":168,"sep":[75,144],"separ":[26,139,148],"seq_len":[69,138],"sequenc":[124,137],"sequenti":[2,52,108,121,137],"serial":[29,120,131,168,172],"serialize_custom":131,"serious":[65,134],"serv":120,"server":[3,25,28,98,112,166,168,171],"servic":140,"service_url":[2,108],"session":[0,2,3,28,29,39,57,60,62,64,65,89,98,101,107,108,112,126,129,131,133,134,139,158,167,171,172],"session_id":[2,26,39,84,108,139,153],"set":[2,3,13,25,29,76,84,100,108,112,134,137,140,145,148,153,166,172,173],"set_level":[13,65,134],"set_optim":[30,99],"set_pref":100,"set_xyt":[64,76,98,133,145,174],"setup":[98,168,169,171],"sever":[120,129],"sfname":[15,143],"sh":[2,25,28,29,60,108,129,166,171,172],"sha":26,"sha256":[2,78,108,147],"shade":145,"shape":[13,60,115,120,129,134,137,152],"shape1":[13,134],"shape2":[13,134],"shapeerror":[13,65,134],"shapiro":[87,156],"share":[3,28,110,112,120,139,163,168],"sharp":[54,123],"sharpen":[115,129],"shell":[25,29,57,98,102,126,166,168,172,173],"shellcheck":102,"shellout":148,"shibboleth":[82,151],"ship":[100,163],"short":[3,109,112,131,148],"shortcut":[60,129],"should_use_temp":114,"show":[13,18,39,64,84,98,108,120,133,134,135,139,145,147,149,153,167,174],"show_annot":145,"shuffl":124,"si":[13,65,134],"side":[52,120,121],"side_effect":120,"sideeffect":120,"sigma":[69,138],"sigmacro_process_figure_":[127,129],"sigmacro_processfigure_":[127,129],"sigmacro_to_blu":[127,129],"sigmacro_toblu":[127,129],"sigmaplot":127,"sign":[87,156],"signal":[29,54,60,62,76,98,116,123,129,168,172],"signatur":[61,84,109,120,130,153,174],"signature_color":109,"signific":[76,87,101,156],"silent":137,"simpl":[18,26,114,120,131,140,145,147,149],"simplex":115,"simpli":169,"sin":[28,54,98,123,174],"sinc":[2,26,108],"sine":[28,84,98,153,174],"singl":[2,28,30,52,99,100,108,120,121,124,131,137,139,158,168,171,174],"singleton":[43,78,112,147],"singular":[89,158,167],"sink":124,"sit":137,"size":[2,13,15,28,29,87,98,108,115,131,134,137,143,156,171,172,174],"skeleton":100,"skip":[100,120,137,148],"skip_hidden":100,"skipped_ahead":120,"skipped_diverg":120,"sklearn":[18,147,149],"slack":140,"slack_webhook":140,"slash":[15,143],"sleep":[116,124],"sleeper":124,"slot":137,"slow":[2,108,148],"slower":[29,172],"small":115,"smi":148,"smirnov":[87,156],"smooth":[69,138],"smtp":140,"smtp_host":140,"snake_cas":[64,133],"snap":[98,167,174],"snippet":[89,158],"sns_barplot":145,"sns_boxplot":145,"sns_heatmap":145,"sns_histplot":145,"sns_jointplot":145,"sns_kdeplot":145,"sns_lineplot":145,"sns_pairplot":145,"sns_scatterplot":145,"sns_stripplot":145,"sns_swarmplot":145,"sns_violinplot":145,"sobel":115,"social":[0,28,29,60,98,129,169,171,172,173],"socialia":[29,169,172],"softwar":28,"someth":[65,134],"sort":[60,120,129,139],"sort_bi":[82,151],"sourc":[2,3,13,15,18,25,39,60,61,64,76,100,102,108,109,111,112,114,115,120,124,127,129,130,133,134,137,139,141,142,143,147,148,149,152,163,166,167,168,173],"source_fil":[2,108],"source_sess":[2,108],"space":[15,52,76,121,143],"spare":[2,108],"spath":[15,30,99,143],"spatialattent":[69,138],"speak":[98,167,174],"spearman":[87,156],"spec":[2,52,76,108,121,148],"specgram":76,"special":[28,76,171],"specif":[2,3,13,18,25,26,39,60,64,82,107,108,112,116,120,129,133,134,137,140,147,148,149,151,158,166,167,168,174],"specifi":[13,15,100,101,114,134,137,143,145],"specified_path":114,"spectral":[54,69,76,123,138],"spectrogram":[69,138],"spectrum":76,"speech":[29,98,101,167,169,172,173,174],"sphinx":[29,120,163,172],"spi":76,"spike":145,"spike_tim":[27,170],"spike_times_list":145,"spine":145,"split":[15,18,50,119,129,137,143,147,149],"sqlalchemi":[29,172],"sqlite":[2,26,62,108],"sqlite3":[2,108],"squar":[87,156],"src":[3,15,98,112,120,141,143,168,173],"src_path":100,"ss":[18,147,149],"ss_xxxx":[84,153],"ssh":[29,120,172],"st":[64,133],"stabl":[3,112],"stack":[13,15,134,143],"stacklevel":[13,134],"stackplot":76,"stadium":[52,121],"stage":[26,52,116,121],"stair":76,"stamp":[2,108],"stamp_id":[2,108],"standalon":[2,3,29,64,76,97,100,108,111,112,115,120,131,133,140,141,148,152,166,168,169,172],"standard":[3,25,27,30,76,99,100,112,120,137,145,148,166,170,173,174],"star":[76,87,156],"start":[2,3,39,54,57,84,89,108,112,115,123,126,129,137,145,148,153,158,167,173],"start_datetim":[84,153],"starts_1d":145,"startup":[3,112,168],"stash":120,"stat":[0,2,28,29,39,60,61,64,65,89,98,108,120,129,130,133,134,158,168,169,171,172,173,174],"stat_annot":76,"state":[2,3,18,78,107,108,112,116,147,149],"state_chang":120,"state_dict":[26,62],"statement":[13,134],"static":[98,163,168],"statist":[2,13,28,29,39,52,60,64,89,98,108,116,120,121,129,131,133,134,148,158,167,168,169,171,172,173],"stats_plot":174,"stats_recommend_test":173,"stats_run_test":[168,173],"statserror":[13,65,134],"statsmodel":[29,172],"status":[2,18,25,26,60,82,84,102,110,116,120,129,140,147,149,151,153,166],"stay":[120,148],"std":[87,156],"stderr":[13,25,26,65,84,98,114,120,134,153,166,168,174],"stdin":[64,133],"stdlib":[2,61,100,108,120,130,141],"stdout":[13,25,26,65,84,98,114,120,134,153,158,166,168,174],"stem":[76,139],"step":[26,27,28,30,76,99,120,170,173],"stft":[69,138],"still_valid_claim":[2,108],"stop":[3,30,99,112],"stop_at":[3,112],"stopper":[30,99],"storag":[2,29,60,100,108,129,172],"store":[2,39,100,108,110,137],"str":[0,2,3,13,15,18,25,28,60,62,84,100,102,108,112,114,115,120,124,129,134,137,139,142,143,147,148,149,152,153,163,166,171],"strategi":[2,25,60,108,129,166],"stream":[13,60,116,120,129],"streamplot":76,"strength":115,"strftime":[18,147,149],"strict":[2,108],"strike":137,"string":[2,13,15,18,25,29,57,78,108,120,124,126,134,137,139,143,147,148,149,158,166,172],"strip":[120,145],"strong":[29,172],"structur":[3,15,25,52,60,82,89,100,109,110,112,120,121,129,131,137,143,151,158,173,174],"studi":28,"stx":[2,17,26,27,28,29,60,98,129,145,168,170,171,172,174],"stx_bar":145,"stx_barh":145,"stx_box":145,"stx_conf_mat":[30,99,145],"stx_ecdf":145,"stx_errorbar":145,"stx_fillv":145,"stx_heatmap":[145,174],"stx_imag":145,"stx_joyplot":145,"stx_kde":145,"stx_line":[131,145],"stx_mean_ci":145,"stx_mean_std":[145,174],"stx_median_iqr":145,"stx_raster":145,"stx_rectangl":145,"stx_scatter":145,"stx_scatter_hist":145,"stx_shaded_lin":145,"stx_violin":[145,174],"style":[3,13,60,84,112,120,127,129,134,137,145,153,171],"stylewarn":[13,65,134],"sub":[28,168,169],"subclass":[61,130],"subcommand":109,"subdir":100,"subdirectori":163,"subject":[75,144,174],"submiss":[25,100,166],"submission_resna_2025":137,"submit":[110,120],"submodul":135,"subplot":[28,62,76,98,131,145,168,174],"substitut":[3,26,43,112],"subtitl":137,"subtyp":100,"succeed":[25,120,166],"success":[2,13,25,26,52,65,84,97,108,120,121,131,134,140,153,166],"suffix":[84,153],"suggest":[13,64,98,133,134,167,174],"suit":[120,158,168],"suitabl":[18,120,147,148,149],"sum":[2,108],"summari":[2,25,61,98,102,108,120,130,137,166],"summarize_diff":[129,137],"supplementari":[25,97,166],"supplementary_diff":[25,166],"supplementarycont":[25,166],"supplementarytre":[25,97,166],"support":[2,3,13,18,43,60,84,87,101,108,112,120,129,134,137,145,147,149,153,156],"supports_return_a":120,"suppress":[97,114,166],"suppress_output":[114,129],"sure":[87,156,174],"surfac":[3,76,112,120,137,171],"surround":137,"svc":[30,99],"svg":[26,62,131],"sw":[25,166],"swapchannel":[69,138],"switch":[98,137],"symbol":[15,120,143],"symlink":[15,82,129,131,143,151,152],"symlink_from_cwd":131,"sync":120,"sync_al":120,"sync_host":120,"sync_host_fn":120,"sync_loc":120,"sync_tag":120,"synchron":120,"syntax":[3,43,112,120],"synthesi":101,"sys":[13,65,120,134],"sys_modul":[13,134],"system":[13,15,25,26,29,57,101,126,134,139,140,143,148,166,168,172],"t":[3,13,15,25,54,87,98,112,120,123,124,134,137,143,145,148,156,166,174],"tab":148,"tab1":[97,166],"tabl":[2,25,28,87,97,108,137,156,166],"table_caption_prefix":137,"tabular":131,"tag":[25,120,166],"take":[3,29,101,107,112,137,167,172,174],"target":[2,15,108,115,120,135,137,143],"target_dir":100,"target_fil":[2,39,108],"target_is_directori":[15,143],"task":[30,99,124,127,140,158,174],"tau":[87,156],"tea":109,"tee":[13,65,134],"temp":114,"templat":[0,13,25,28,60,65,97,98,100,129,134,137,166,171,173],"template_clone_templ":158,"template_get_code_templ":158,"template_list_code_templ":158,"template_path":137,"template_typ":158,"templateerror":[13,65,134],"templateviolationerror":[13,65,134],"tempor":[2,108],"temporari":[18,65,78,134,147,149],"temporarili":[13,134],"temporary_se":[18,78,147,149],"tend":168,"tensor":[18,50,119,147,149],"tensorflow":[78,147],"tensorpac":[29,172],"term":[28,120],"termin":[120,135],"test":[2,3,13,15,18,28,29,30,52,54,60,61,84,98,99,107,108,112,121,123,124,129,130,134,143,147,149,152,153,158,167,168,169,171,172,173,174],"test_anova":[87,156],"test_anova_2way":[87,156],"test_anova_rm":[87,156],"test_brunner_munzel":[87,156],"test_chi2":[87,156],"test_cochran_q":[87,156],"test_fish":[87,156],"test_friedman":[87,156],"test_kendal":[87,156],"test_krusk":[87,156],"test_ks_1samp":[87,156],"test_ks_2samp":[87,156],"test_mannwhitneyu":[87,156],"test_mcnemar":[87,156],"test_monitor":[3,112],"test_nam":[13,134],"test_norm":[87,156],"test_pearson":[87,98,156],"test_shapiro":[87,156],"test_siz":[18,147,149],"test_spearman":[87,156],"test_theilsen":[87,156],"test_ttest_1samp":[87,156],"test_ttest_ind":[61,87,98,130,156],"test_ttest_rel":[87,156],"test_wilcoxon":[87,156],"testabl":148,"testerror":[13,65,134],"tex":[2,28,60,87,97,108,129,137,156,166,171],"tex_stem":137,"text":[2,13,26,29,57,76,98,100,108,109,115,120,126,129,131,134,135,137,152,167,169,172,173,174],"text_a":137,"text_b":137,"tf":[18,147,149],"tgt":141,"theil":[87,156],"theme":100,"themselv":[2,108],"theta":[54,69,123,138],"thick":115,"thin":[120,168],"third":[61,130],"this_path":[15,129,143],"thorough":[2,39,108],"thrash":[29,172],"thread":[39,108,142],"threadpoolexecutor":142,"three":[25,26,28,166,171,174],"thresh":115,"threshold":[84,115,120,129,153],"tie":26,"tiff":[62,131],"tight":[52,121],"tighter":[52,121],"time":[2,25,28,50,54,76,97,98,108,119,123,124,139,145,148,163,166,174],"time_format":[18,78,147,149],"timelin":111,"timeout":[2,25,50,61,97,108,119,120,130,166],"timeouterror":[50,119],"timepoint":145,"timestamp":[2,18,57,78,108,126,129,137,139,147,148,149,152,168,174],"tip":[25,115,166],"tip_length":115,"titl":[52,76,82,97,98,116,120,121,137,140,145,151,166,173],"title2path":[57,126],"tmp":[15,120,143,148],"tmp_path":[2,108],"to_bgr":[115,129],"to_csv":168,"to_datafram":[82,151],"to_dict":120,"to_graphviz":[52,121],"to_gray":[115,129],"to_json":120,"to_mermaid":[52,121,139],"to_rgb":[115,129],"to_script":139,"to_xyz":[75,144],"togeth":26,"toggl":137,"token":[3,112,137],"toml":[2,64,108,131,133,158],"tool":[2,18,25,26,28,29,54,64,100,102,108,109,110,120,123,133,139,147,149,158,163,166,167,168,169,171,172,174],"toolkit":[168,169,171],"top":[3,52,112,115,121,145,148],"top_rec":[82,151],"topo":[2,108],"topolog":[2,108],"torch":[18,26,28,29,50,60,69,78,119,129,138,147,149,171,172],"torch_fn":[50,119],"torch_gen":[78,147],"total":[13,82,120,124,134,151],"touch":168,"tozero":115,"tozero_inv":115,"tpng":[52,121],"tqdm":[50,119],"trace":[2,26,39,108],"track":[2,17,18,25,27,28,30,39,60,76,84,97,98,99,108,131,137,145,147,149,153,166,167,168,170],"track_chang":[25,97,166],"trackchang":137,"traffic":148,"trail":[15,137,143],"train":[26,60,69,101,110,129,138,140,168],"train_data":[18,78,147,149],"train_model":[101,140],"train_out":26,"train_test_split":[18,147,149],"transform":[54,69,75,115,123,138,144,174],"transit":[29,172],"translat":[13,134],"translator_nam":[13,134],"translatorerror":[13,134],"transposelay":[69,138],"travers":[3,100,112],"treat":120,"treatment":[87,156,173],"tree":[26,52,60,97,100,109,121,129,131,166,168],"tricontour":76,"tricontourf":76,"trigger":[84,120,153],"tripcolor":76,"triplex":115,"triplot":76,"trivial":[2,108],"true":[2,3,13,15,18,25,39,61,76,82,84,97,98,100,108,109,112,114,115,120,124,130,131,134,137,140,143,145,147,148,149,151,152,153,166],"trunc":115,"truth":[2,108,137],"try_import_opt":120,"ts":[28,163],"tsa":[2,108],"tsv":131,"ttest_ind":[168,174],"tts":[101,168],"tukey":[87,156],"tunnel":[28,29,60,129,171,172],"tupl":[13,15,25,109,114,115,124,134,137,142,143,166],"turn":98,"turquois":137,"twitter":169,"two":[3,13,87,112,134,137,156],"txt":[13,15,62,131,134,143,152],"type":[2,3,13,15,18,25,26,28,60,62,82,84,100,102,108,109,112,114,115,124,129,134,137,139,142,143,147,148,149,151,152,153,163,166,167,168,171,174],"typescript":[28,163],"typic":[29,98,129,169,172,173],"u":[27,87,156,170],"ui":[28,60,98,129,171,173],"umap":[30,99],"underlin":137,"underlying":[120,168,174],"understand":168,"undoabl":120,"unexpect":[65,134],"unifi":[2,28,29,30,65,99,100,102,108,109,116,120,131,134,139,167,169,171,172],"union":[2,3,13,15,100,108,112,115,134,137,139,143],"uniqu":[2,18,75,78,82,84,108,144,147,149,151,153],"unit":[13,26,65,76,134],"unitwarn":[13,65,134],"universal":[3,28,62,98,112],"universiti":140,"unknown":[39,108,114,137],"unlik":120,"unlink":137,"unlink_symlink":[15,129,143],"unmatch":137,"unpush":120,"unset":[2,108],"untouch":137,"untrack":139,"unwrap":[120,137],"updat":[15,25,116,120,143,166],"upload":110,"upper":145,"upper_cas":133,"uppercas":[3,112],"upstream":[2,108,120],"upward":[3,112],"url":[2,13,82,108,120,134,140,151],"usabl":[3,112],"usag":[3,13,17,18,25,29,60,61,102,112,129,130,134,135,147,148,149,152,171,172],"use":[2,3,13,18,25,26,39,43,64,76,78,84,87,89,98,100,108,111,112,114,120,124,127,129,131,133,134,137,139,140,142,147,148,149,152,153,156,158,163,166,168,169,173,174],"use_temp":114,"user":[2,3,15,28,43,108,112,114,120,135,137,143,148,168],"user_provided_path":[3,112],"userwarn":13,"usr":163,"usual":[2,108],"utc":137,"util":[3,15,18,28,29,30,54,57,78,99,107,115,120,123,124,126,127,137,142,143,147,148,149,152,171,172,174],"v":[27,170],"v1":111,"v12":127,"v15":137,"v16":137,"v2":[84,111,153],"val_loss":[30,99],"valid":[13,28,30,57,99,100,126,134,137],"validate_docu":[129,137],"valu":[2,3,13,18,27,43,65,75,76,84,87,98,102,108,112,115,120,124,134,144,145,147,149,153,156,168,170,174],"value_col":[87,156],"valueerror":[2,108,120],"values_1d":145,"values_2d":145,"values_list":145,"var":[2,3,26,43,100,108,112],"variabl":[2,3,13,43,61,108,112,130,134,148,168],"variant":[2,108],"various":[135,148],"vector":[28,76,171],"ver":120,"verbos":[2,13,18,78,84,108,134,147,148,149,153],"verif":[2,18,26,60,62,98,129,131,139,147,149,169,173],"verifi":[2,18,25,26,28,39,78,98,100,108,139,147,149,166,168,171,173,174],"verify_claim":[2,108],"verify_host":[129,141],"verify_notebook":[129,139],"verify_structur":[25,166],"version":[13,15,18,29,65,98,100,116,120,134,143,147,149,167,172,173],"version_prefix":[15,143],"vertic":[115,145],"via":[2,15,29,54,60,64,76,84,98,100,108,120,123,129,133,137,139,140,143,148,153,158,168,169,171,172],"view":[2,108,137,167],"violat":[13,64,133,134],"violin":[145,174],"violinplot":76,"viridi":145,"visibl":[52,121],"vision":[29,172],"visual":[2,26,39,60,76,98,107,108,129,139,174],"vite":163,"viz":109,"volum":[82,151],"vram":148,"vs":[13,26,29,100,114,131,134,172],"w":[13,27,131,134,137,170,174],"w_id":137,"walk":[3,29,109,112,137,172],"walk_up":[3,112],"walkthrough":171,"walli":[87,156],"want":[29,120,139,172],"warn":[13,18,25,29,52,60,64,111,121,129,133,137,147,149,166,172],"warn_data_loss":[13,134],"warn_deprec":[13,65,134],"warn_perform":[13,134],"watch":[25,166],"wav":[101,131],"wave":[54,98,123,174],"waveform":116,"wavelet":[54,69,123,138],"way":[87,156],"web":[28,29,52,60,121,129,171,172],"webhook":140,"webpack":163,"weight":[18,30,69,99,131,138,147,149,168],"well":173,"wherev":137,"whether":[2,3,13,18,25,100,108,112,115,120,134,137,147,148,149,166],"whi":168,"whitney":[87,156],"whose":[2,108,120,137,139],"wide":148,"width":[52,107,115,121,145],"wilcoxon":[87,156],"wildcard":133,"wilk":[87,156],"will":[114,137,148,168],"win":[3,112],"window":[15,140,143],"within":[15,100,120,143,148],"without":[15,84,114,116,120,124,137,139,143,153,168],"word":137,"wordread":[129,137],"wordwrit":[129,137],"work":[3,26,28,29,60,76,82,87,100,111,112,114,120,129,151,156,163,168,172,173,174],"worker":[82,151],"workflow":[28,52,89,109,110,120,121,158,168,171,174],"workspac":[28,100],"wrap":[13,17,26,27,50,64,76,84,97,109,119,120,133,134,137,139,153,166,170,171,174],"wrap_as_c":120,"wrap_as_mcp":120,"wrap_as_tracked_delet":[129,137],"wrap_as_tracked_insert":[129,137],"wrapper":[15,18,30,99,111,129,137,143,147,149],"write":[2,13,25,29,84,100,102,108,131,134,137,153,158,169,171,172],"write_fil":100,"writer":[0,3,28,29,60,98,109,112,129,137,158,168,169,171,172,173],"writer_compile_manuscript":173,"writer_doc":137,"written":[2,108,137],"x":[13,18,27,28,50,62,75,76,84,87,98,107,111,115,119,124,131,134,142,144,145,147,149,153,156,170,174],"x1":76,"x2":76,"x_end":[27,170],"x_label":145,"x_start":[27,170],"x_test":[18,147,149],"x_train":[18,147,149],"xarray":[50,119],"xarray_fn":[50,119],"xcorr":76,"xerr":145,"xlabel":[76,145],"xls":131,"xlsx":131,"xml":137,"xs":145,"xx":145,"xxxx":[84,153],"xy":[27,170],"xyz":145,"y":[18,27,28,62,75,76,87,98,107,115,124,131,142,144,145,147,149,156,170,174],"y1":[27,170],"y2":[27,170],"y_clean":98,"y_label":145,"y_mean":[27,170],"y_noisi":98,"y_pred":[30,99],"y_proba":[30,99],"y_std":[27,170],"y_true":[30,99],"yaml":[2,3,26,28,29,60,62,76,82,84,98,100,108,129,148,151,153,158,168,171,172,174],"year":[82,151],"year_min":[82,151],"yerr":[27,145,170],"yet":[13,50,119,134],"yield":[13,124,134],"yield_grid":[124,129],"ylabel":[76,145,173],"yml":[26,62,131],"ys_lower":145,"ys_middl":145,"ys_upper":145,"ywatanabe1989":[29,172],"yy":[18,145,147,149],"yyyi":[13,18,84,134,147,149,153],"z":[27,75,144,170],"zarr":129,"zenodo":[2,108],"zero":[2,18,84,100,108,120,147,149,153],"zotero":[13,134],"zshrc":168},"titles":["API Reference","scitex.ai API Reference","scitex.clew API Reference","scitex.config API Reference","scitex.db API Reference","scitex.decorators API Reference","scitex.diagram API Reference","scitex.dict API Reference","scitex.dsp API Reference","scitex.gen API Reference","scitex.introspect API Reference","scitex.io API Reference","scitex.linter","scitex.logging API Reference","scitex.nn API Reference","scitex.path API Reference","scitex.pd API Reference","scitex.plt API Reference","scitex.repro API Reference","scitex.scholar API Reference","scitex.session API Reference","scitex.social API Reference","scitex.stats API Reference","scitex.str API Reference","scitex.template API Reference","scitex.writer","Core Concepts","Plot Gallery","SciTeX \u2013 Modular Python Toolkit for Researchers and AI Agents","Installation","AI Module (stx.ai)","app Module (stx.app)","audio Module (stx.audio)","audit Module (stx.audit)","benchmark Module (stx.benchmark)","bridge Module (stx.bridge)","browser Module (stx.browser)","canvas Module (stx.canvas)","capture Module (stx.capture)","Clew Module (stx.clew)","cli Module (stx.cli)","cloud Module (stx.cloud)","compat Module (stx.compat)","Config Module (stx.config)","container Module (stx.container)","context Module (stx.context)","cv Module (stx.cv)","dataset Module (stx.dataset)","datetime Module (stx.datetime)","db Module (stx.db)","Decorators Module (stx.decorators)","dev Module (stx.dev)","Diagram Module (stx.diagram)","dict Module (stx.dict)","DSP Module (stx.dsp)","etc Module (stx.etc)","events Module (stx.events)","Gen Module (stx.gen)","gists Module (stx.gists)","git Module (stx.git)","Module Overview","Introspect Module (stx.introspect)","IO Module (stx.io)","linalg Module (stx.linalg)","Linter Module (stx.linter)","Logging Module (stx.logging)","media Module (stx.media)","module Module (stx.module)","msword Module (stx.msword)","NN Module (stx.nn)","notebook Module (stx.notebook)","notify Module (stx.notify)","os Module (stx.os)","parallel Module (stx.parallel)","path Module (stx.path)","PD Module (stx.pd)","PLT Module (stx.plt)","project Module (stx.project)","Repro Module (stx.repro)","resource Module (stx.resource)","rng Module (stx.rng)","schema Module (stx.schema)","Scholar Module (stx.scholar)","security Module (stx.security)","Session Decorator (@stx.session)","sh Module (stx.sh)","social Module (stx.social)","Stats Module (stx.stats)","str Module (stx.str)","Template Module (stx.template)","tex Module (stx.tex)","torch Module (stx.torch)","tunnel Module (stx.tunnel)","types Module (stx.types)","ui Module (stx.ui)","utils Module (stx.utils)","web Module (stx.web)","Writer Module (stx.writer)","Quick Start","AI Module (stx.ai)","App Module (stx.app)","Audio Module (stx.audio)","audit Module (stx.audit)","benchmark Module (stx.benchmark)","bridge Module (stx.bridge)","browser Module (stx.browser)","canvas Module (stx.canvas)","Capture Module (stx.capture)","Clew Module (stx.clew)","cli Module (stx.cli)","Cloud Module (stx.cloud)","compat Module (stx.compat)","Config Module (stx.config)","container Module (stx.container)","context Module (stx.context)","cv Module (stx.cv)","Dataset Module (stx.dataset)","datetime Module (stx.datetime)","db Module (stx.db)","Decorators Module (stx.decorators)","Dev Module (stx.dev)","Diagram Module (stx.diagram)","dict Module (stx.dict)","DSP Module (stx.dsp)","etc Module (stx.etc)","events Module (stx.events)","Gen Module (stx.gen)","gists Module (stx.gists)","git Module (stx.git)","API Reference","Introspect Module (stx.introspect)","I/O Module (stx.io)","linalg Module (stx.linalg)","Linter Module (stx.linter)","Logging Module (stx.logging)","media Module (stx.media)","module Module (stx.module)","msword Module (stx.msword)","NN Module (stx.nn)","notebook Module (stx.notebook)","Notification Module (stx.notification)","os Module (stx.os)","parallel Module (stx.parallel)","path Module (stx.path)","PD Module (stx.pd)","scitex.plt","project Module (stx.project)","Repro Module (stx.repro)","resource Module (stx.resource)","rng Module (stx.rng)","schema Module (stx.schema)","Scholar Module (stx.scholar)","security Module (stx.security)","Session Decorator (@stx.session)","sh Module (stx.sh)","social Module (stx.social)","Stats Module (stx.stats)","str Module (stx.str)","Template Module (stx.template)","tex Module (stx.tex)","torch Module (stx.torch)","tunnel Module (stx.tunnel)","types Module (stx.types)","ui Module (stx.ui)","utils Module (stx.utils)","web Module (stx.web)","Writer Module (stx.writer)","CLI Reference","Core Concepts","Ecosystem","Plot Gallery","SciTeX Documentation","Installation","MCP Server","Quickstart"],"titleterms":{"How":[39,84,108,153],"I":[26,60,129,131,168,174],"It":[39,84,108,153],"Other":[30,60,62,99,129],"The":26,"What":[158,173],"You":158,"access":[110,116,140],"agent":[28,98],"ai":[1,28,30,98,99],"analysi":[60,61,129,130,174],"annot":76,"api":[0,1,2,3,4,5,6,7,8,9,10,11,13,14,15,16,17,18,19,20,21,22,23,24,30,39,43,50,52,54,57,61,62,64,65,69,75,76,78,82,84,87,89,97,98,99,100,101,107,108,110,112,116,119,120,121,123,126,129,130,131,133,134,138,140,144,147,151,153,156,158,166],"app":[31,100],"applic":100,"architectur":[26,69,82,133,138,151,168],"area":[27,170],"attent":[69,138],"audio":[32,101,167],"audit":[33,102],"auto":[50,119],"avail":[54,75,78,87,123,137,144,147,156],"axi":[76,145],"backend":[52,101,121,140],"bar":145,"base":76,"basic":[84,153],"batch":[50,119],"benchmark":[34,103],"best":[26,84,153],"bridg":[35,104],"browser":[36,105],"bulk":120,"cach":[50,119,131],"canva":[37,106],"captur":[38,107,167],"case":[101,107],"categor":[27,145,170],"categori":[64,65,133,134,173],"chain":[39,108],"channel":[69,138],"class":[43,82,97,112,151,166],"classif":[30,99],"clew":[2,26,39,108],"cli":[39,40,82,97,98,108,109,110,116,120,140,151,166,167,174],"cloud":[41,110],"code":[61,89,130,158],"command":[39,98,108],"comparison":[87,156],"compat":[42,111],"compil":[97,166],"complet":98,"composit":76,"concept":[26,168],"config":[3,43,84,112,131,140,153],"configur":[26,43,64,112,133,168],"contain":[44,113],"context":[45,114],"contour":[27,170],"convers":[50,119],"core":[26,29,60,129,168,172],"correct":[87,156],"creat":100,"creation":145,"crop":107,"cv":[46,115],"data":[60,82,87,110,129,151,156],"databas":116,"dataset":[47,116],"datetim":[48,117],"db":[4,49,118],"decor":[5,50,84,119,153,168,174],"deleg":[168,169],"depend":[39,108],"descript":[87,156],"dev":[51,120,167],"develop":[29,129,172],"diagram":[6,52,121],"dict":[7,53,122],"directori":[84,153],"distribut":[27,145,170],"document":[61,97,120,130,166,171],"dsp":[8,54,123],"durat":107,"ecosystem":[28,120,169],"emphasi":[52,121],"etc":[55,124],"event":[56,125],"everyth":[29,172],"exampl":[2,3,13,15,18,25,98,108,112,114,115,120,124,133,134,137,139,142,143,147,148,149,166,173],"except":[65,134],"explor":131,"extra":[29,172],"featur":[100,110,171],"fetch":116,"figur":[62,76,145,174],"file":[110,174],"filter":[82,151],"format":[62,87,131,156],"four":28,"freedom":28,"friend":120,"function":[39,43,54,62,75,78,101,107,108,112,116,123,131,140,144,147],"galleri":[27,145,170],"gen":[9,57,126],"general":167,"get":[28,158,171],"gist":[58,127],"git":[59,89,128,158],"grid":[27,170],"guid":171,"guidelin":[97,166],"hdf5":131,"heatmap":[27,145,170],"helper":76,"hierarchi":[65,134],"hoc":[87,156],"hot":120,"hpc":120,"imag":107,"implement":100,"indic":171,"infrastructur":[60,129],"inject":[84,153],"instal":[29,169,172],"integr":140,"interfac":[64,98,133,158,168,171],"interval":107,"introspect":[10,61,130,167],"io":[11,62,131],"ipython":[61,130],"job":110,"key":[39,43,82,97,100,101,107,108,110,112,116,131,140,151,166,171],"kwarg":[101,116,131,140],"layer":[69,138],"layout":76,"learn":[60,129],"level":[39,64,65,108,133,134],"linalg":[63,132],"line":[27,145,170],"linter":[12,64,133],"list_backend":[101,140],"list_format":131,"list_sourc":116,"literatur":[60,129,174],"llm":120,"load":131,"load_config":131,"local":116,"log":[13,65,134],"machin":[60,129],"manag":[43,97,110,112,120,166,174],"manipul":[69,138],"mcp":[98,120,158,173],"media":[66,135],"messag":140,"metadata":[82,151],"method":145,"metric":[30,99],"mm":76,"mode":[52,121],"model":26,"modul":[29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,85,86,87,88,89,90,91,92,93,94,95,96,97,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,146,147,148,149,150,151,152,154,155,156,157,158,159,160,161,162,163,164,165,166,172],"modular":[28,168],"monitor":107,"msword":[68,137],"multipl":[87,156],"next":98,"nn":[14,69,138],"node":[52,121],"none":[101,107,116,140],"note":137,"notebook":[70,139],"notif":140,"notifi":71,"o":[26,60,129,131,168,174],"obj":131,"object":[84,153],"oper":110,"option":[84,97,153,166],"order":[50,119],"organiz":[82,151],"os":[72,141],"output":[84,87,133,153,156],"output_dir":107,"overview":[60,100,110,120],"packag":[120,169],"paper":[52,82,121,151],"parallel":[73,142],"paramet":[84,87,153,156],"path":[15,43,74,101,112,131,143],"pattern":[131,168,169],"pd":[16,75,144],"per":[29,172],"play":101,"plot":[27,76,145,170],"plt":[17,76,145],"plugin":133,"post":[87,156],"practic":[26,84,153],"preset":76,"prioriti":[43,112],"process":[50,69,119,138],"profil":137,"project":[77,82,89,97,146,151,158,166],"proven":[26,62],"public":174,"python":[28,89,98,158],"queri":116,"quick":[30,43,50,52,54,57,61,62,64,65,69,75,76,78,82,87,97,98,99,100,101,107,110,112,116,119,121,123,126,130,131,133,134,138,140,144,147,151,156,166],"quickstart":174,"randomstatemanag":[78,147],"readi":174,"recommend":[29,87,156,172],"redirect":[65,134],"refer":[0,1,2,3,4,5,6,7,8,9,10,11,13,14,15,16,17,18,19,20,21,22,23,24,30,39,43,50,52,54,57,61,62,64,65,69,75,76,78,82,84,87,89,97,99,100,101,107,108,110,112,116,119,120,121,123,126,129,130,131,133,134,138,140,144,147,151,153,156,158,166,167,169,171],"region":107,"registri":131,"reload":120,"renam":120,"repositori":110,"repro":[18,78,147],"reproduc":76,"requir":[29,172],"research":[26,28,29,172],"resolut":[43,112],"resourc":[79,148],"rng":[80,149],"role":28,"rule":[64,133],"save":[62,131],"scatter":[27,145,170],"schema":[81,150],"scholar":[19,82,151,167],"scienc":[60,129],"scitex":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,28,145,171],"seaborn":[87,145,156],"search":116,"secur":[83,152],"send":140,"server":173,"session":[20,26,84,140,153,168,174],"setup":173,"sever":[64,133],"sh":[85,154],"shape":[52,69,121,138],"shortcut":[61,130],"signal":[69,138],"snap":107,"social":[21,86,155],"sort":[82,151],"sourc":[82,116,151],"speak":101,"special":[27,145,170],"specif":[52,121],"start":[28,98,100,101,107,110,116,131,133,140,171],"stat":[22,87,156,167],"statist":[27,76,87,145,156,170,174],"status":[39,108],"step":98,"storag":[82,110,151],"str":[23,88,157],"strategi":[89,137,158],"stream":[65,134],"structur":[84,97,153,166],"stx":[30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166],"style":[61,76,87,130,156],"support":[62,116,131,140],"tabl":171,"templat":[24,89,158,167],"test":[87,120,156],"tex":[90,159],"text":101,"three":[98,168],"tool":[98,173],"toolkit":28,"torch":[91,160],"track":[26,62],"train":[30,99],"tree":[61,130],"tunnel":[92,161],"type":[50,52,61,76,93,119,120,121,130,162],"typic":137,"ui":[94,163],"unifi":[168,174],"universal":26,"usag":[82,84,97,137,151,153,166,174],"use":[29,101,107,172],"util":[43,50,60,95,112,119,129,145,164],"uv":[29,172],"vector":[27,170],"verif":[39,108],"verifi":[29,172],"visual":[30,99],"warn":[65,134],"web":[96,165],"work":[39,84,108,153],"workflow":[26,29,172,173],"wrapper":[120,145],"write":[60,97,129,166],"writer":[25,97,166],"yaml":[43,52,112,121,131],"zarr":131}}) \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/ai.html b/src/scitex/_sphinx_html/source/api/ai.html new file mode 100644 index 000000000..bd9d20579 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/ai.html @@ -0,0 +1,753 @@ + + + + + + + + + AI Module (stx.ai) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

AI Module (stx.ai)

+

Machine learning utilities for training, classification, and metrics +with PyTorch and scikit-learn.

+
+

Quick Reference

+
import scitex as stx
+
+# Training utilities
+from scitex.ai import LearningCurveLogger, EarlyStopping
+
+logger = LearningCurveLogger()
+stopper = EarlyStopping(patience=10, direction="minimize")
+
+for epoch in range(100):
+    # ... training loop ...
+    logger({"loss": loss, "acc": acc}, step="Training")
+    if stopper(val_loss, {"model": model_path}, epoch):
+        break
+
+logger.plot_learning_curves(spath="curves.png")
+
+# Classification
+from scitex.ai import ClassificationReporter, Classifier
+
+clf = Classifier()("SVC")
+reporter = ClassificationReporter(output_dir="./results")
+reporter.calculate_metrics(y_true, y_pred, y_proba)
+reporter.save_summary()
+
+
+
+
+

Training

+
    +
  • LearningCurveLogger – Track and visualize training/validation/test metrics across epochs

  • +
  • EarlyStopping – Monitor validation metrics and stop when improvement plateaus

  • +
+
+
+

Classification

+
    +
  • ClassificationReporter – Unified reporter for single/multi-task classification (balanced accuracy, MCC, ROC-AUC, confusion matrices)

  • +
  • Classifier – Factory for scikit-learn classifiers (SVC, KNN, Logistic Regression, AdaBoost, …)

  • +
  • CrossValidationExperiment – Cross-validation framework

  • +
+
+
+

Metrics

+

Standardized calc_* functions:

+
    +
  • calc_bacc – Balanced accuracy

  • +
  • calc_mcc – Matthews Correlation Coefficient

  • +
  • calc_conf_mat – Confusion matrix

  • +
  • calc_roc_auc – ROC-AUC score

  • +
  • calc_pre_rec_auc – Precision-Recall AUC

  • +
  • calc_feature_importance – Feature importance scores

  • +
+
+
+

Visualization

+
    +
  • plot_learning_curve – Training/validation curves

  • +
  • stx_conf_mat – Confusion matrix heatmap

  • +
  • plot_roc_curve – ROC curve

  • +
  • plot_pre_rec_curve – Precision-Recall curve

  • +
  • plot_feature_importance – Feature importance bar plots

  • +
+
+
+

Other

+
    +
  • MultiTaskLoss – Multi-task learning loss weighting

  • +
  • get_optimizer / set_optimizer – Optimizer management

  • +
  • GenAI – Generative AI wrapper (lazy-loaded)

  • +
  • Clustering: pca, umap

  • +
+
+
+

API Reference

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/app.html b/src/scitex/_sphinx_html/source/api/app.html new file mode 100644 index 000000000..af7ae4b3b --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/app.html @@ -0,0 +1,1094 @@ + + + + + + + + + App Module (stx.app) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

App Module (stx.app)

+

Runtime SDK for SciTeX applications. Provides a unified interface for +file storage, configuration, and lifecycle management that works +identically in local and cloud environments.

+
+

Note

+

stx.app delegates to the standalone +scitex-app package. +Install with: pip install scitex-app.

+
+
+

Overview

+

SciTeX applications are self-contained scientific tools that can run +locally or on the SciTeX cloud platform. The stx.app module +provides the runtime SDK that each application uses to interact with +its environment.

+
+
+

Quick Start

+
import scitex as stx
+
+# Get current application info
+info = stx.app.get_info()
+
+# Access application preferences
+prefs = stx.app.get_prefs()
+
+# Check dependencies
+stx.app.check_deps()
+
+
+
+
+

Key Features

+
+
Unified File Storage

Read and write files through a single API that abstracts local +filesystem and cloud storage.

+
# These work identically locally and in the cloud
+stx.app.write_file("results/output.csv", data)
+content = stx.app.read_file("config/settings.yaml")
+
+
+
+
Configuration Management

Application preferences are stored in a standard location and +accessible via dot-notation.

+
prefs = stx.app.get_prefs()
+print(prefs.theme)
+print(prefs.default_format)
+
+stx.app.set_prefs(theme="dark", default_format="pdf")
+
+
+
+
Application Lifecycle

Query and manage the running application.

+
info = stx.app.get_info()
+print(info.name)
+print(info.version)
+
+current = stx.app.get_current()    # Currently active app
+
+
+
+
Dependency Checking

Verify that all required packages are available.

+
missing = stx.app.check_deps()
+if missing:
+    print(f"Missing: {missing}")
+
+
+
+
+
+
+

Creating an Application

+

Scaffold a new SciTeX application from a template:

+
scitex template clone app my_tool
+
+
+

This creates a project with bridge-init configuration, MountPoint +definitions, and EventBus integration pre-configured.

+
+
+

API Reference

+

scitex-app — Write-once interface for local + cloud SciTeX apps.

+

Standalone package. Zero dependencies (pure stdlib). +When used with scitex, integration is automatic via scitex.app.

+

Public API (3 functions):

+
from scitex_app.sdk import get_files, register_backend, FilesBackend
+
+# Get a file backend (auto-detects local vs cloud)
+files = get_files("./project")
+
+# Read/write files
+content = files.read("data/config.yaml")
+files.write("output/result.csv", csv_text)
+
+# Register a custom backend
+register_backend("s3", my_s3_factory)
+
+
+
+
+class scitex.app.FilesBackend(*args, **kwargs)[source]
+

Bases: Protocol

+

File storage backend protocol.

+

Implementations must provide these 7 methods. +Uses typing.Protocol for structural subtyping — backends +just implement the methods, no inheritance required.

+
+

Implementations

+
    +
  • FileSystemBackend — local pathlib (ships with scitex-app)

  • +
  • CloudFilesBackend — HTTP via scitex_cloud (provided at runtime)

  • +
+
+
+read(path, *, binary=False)[source]
+

Read file content.

+
+
Parameters:
+
    +
  • path (str) – Relative path within the backend’s namespace.

  • +
  • binary (bool) – If True, return bytes; otherwise return str.

  • +
+
+
Raises:
+

FileNotFoundError – If the file does not exist.

+
+
Return type:
+

Union[str, bytes]

+
+
+
+ +
+
+write(path, content)[source]
+

Write content to a file, creating parent dirs as needed.

+
+
Parameters:
+
    +
  • path (str) – Relative path within the backend’s namespace.

  • +
  • content (Union[str, bytes]) – Text or binary content.

  • +
+
+
Return type:
+

None

+
+
+
+ +
+
+list(directory='', *, extensions=None)[source]
+

List file paths in a directory.

+
+
Parameters:
+
    +
  • directory (str) – Relative directory path (”” = root).

  • +
  • extensions (Optional[List[str]]) – Filter by extension, e.g. [“.yaml”, “.png”].

  • +
+
+
Returns:
+

Relative file paths.

+
+
Return type:
+

List[str]

+
+
+
+ +
+
+exists(path)[source]
+

Check if a file exists.

+
+
Return type:
+

bool

+
+
+
+ +
+
+delete(path)[source]
+

Delete a file.

+
+
Raises:
+

FileNotFoundError – If the file does not exist.

+
+
Return type:
+

None

+
+
+
+ +
+
+rename(old_path, new_path)[source]
+

Rename/move a file within the namespace.

+
+
Raises:
+
+
+
Return type:
+

None

+
+
+
+ +
+
+copy(src_path, dest_path)[source]
+

Copy a file within the namespace.

+
+
Raises:
+

FileNotFoundError – If src_path does not exist.

+
+
Return type:
+

None

+
+
+
+ +
+
+ +
+
+scitex.app.get_files(root=None, *, backend=None, **kwargs)[source]
+

Get a files backend instance.

+

Auto-detection logic:

+
    +
  1. If backend is specified, use that.

  2. +
  3. If SCITEX_API_TOKEN env var is set and “cloud” backend +is registered, use cloud.

  4. +
  5. Otherwise, use filesystem (default).

  6. +
+
+
Parameters:
+
    +
  • root (Union[str, Path, None]) – Root directory for filesystem backend. Defaults to cwd.

  • +
  • backend (Optional[str]) – Explicit backend name. If None, auto-detected.

  • +
+
+
Returns:
+

A backend instance.

+
+
Return type:
+

FilesBackend

+
+
Raises:
+

KeyError – If the requested backend is not registered.

+
+
+
+ +
+
+scitex.app.register_backend(name, factory)[source]
+

Register a files backend factory.

+
+
Parameters:
+
    +
  • name (str) – Backend identifier (e.g., “cloud”, “s3”).

  • +
  • factory (Callable[..., FilesBackend]) – Callable(root, **kwargs) -> FilesBackend instance.

  • +
+
+
Return type:
+

None

+
+
+
+ +
+
+scitex.app.build_tree(backend, directory='', *, extensions=None, skip_hidden=True, max_depth=10)[source]
+

Build a nested tree structure from a FilesBackend.

+
+
Parameters:
+
    +
  • backend (Any) – A file storage backend implementing the FilesBackend protocol.

  • +
  • directory (str) – Starting directory (relative to backend root). Default: root.

  • +
  • extensions (Optional[List[str]]) – Filter files by extension (e.g., [“.yaml”, “.png”]). +Directories are always included for traversal.

  • +
  • skip_hidden (bool) – Skip files/directories starting with “.”. Default: True.

  • +
  • max_depth (int) – Maximum recursion depth to prevent runaway traversal. Default: 10.

  • +
+
+
Returns:
+

Nested tree structure:

+
[
+    {"path": "subdir", "name": "subdir", "type": "directory",
+     "children": [...]},
+    {"path": "file.yaml", "name": "file.yaml", "type": "file"},
+]
+
+
+

+
+
Return type:
+

List[Dict[str, Any]]

+
+
+
+ +
+
+scitex.app.read_file(path, *, root='.', binary=False)[source]
+

Read a single file via the resolved FilesBackend.

+

Equivalent to get_files(root).read(path, binary=binary); +mirrors the app_read_file MCP tool.

+
+
Return type:
+

Union[str, bytes]

+
+
+
+ +
+
+scitex.app.write_file(path, content, *, root='.')[source]
+

Write a file via the resolved FilesBackend.

+

Mirrors the app_write_file MCP tool.

+
+
Return type:
+

None

+
+
+
+ +
+
+scitex.app.list_files(directory='', *, root='.', extensions=None)[source]
+

List file paths under directory.

+

Mirrors the app_list_files MCP tool.

+
+
Return type:
+

List[str]

+
+
+
+ +
+
+scitex.app.file_exists(path, *, root='.')[source]
+

Return whether path exists in the resolved backend.

+

Mirrors the app_file_exists MCP tool.

+
+
Return type:
+

bool

+
+
+
+ +
+
+scitex.app.delete_file(path, *, root='.')[source]
+

Delete path in the resolved backend.

+

Mirrors the app_delete_file MCP tool.

+
+
Return type:
+

None

+
+
+
+ +
+
+scitex.app.copy_file(src_path, dest_path, *, root='.')[source]
+

Copy src_path to dest_path within the resolved backend.

+

Mirrors the app_copy_file MCP tool.

+
+
Return type:
+

None

+
+
+
+ +
+
+scitex.app.rename_file(old_path, new_path, *, root='.')[source]
+

Rename old_path to new_path within the resolved backend.

+

Mirrors the app_rename_file MCP tool.

+
+
Return type:
+

None

+
+
+
+ +
+
+scitex.app.scaffold(target_dir='.', *, name=None, label=None, icon='fas fa-puzzle-piece', description='', frontend='html', overwrite=False)[source]
+

Generate a new SciTeX workspace app skeleton.

+

Mirrors the app_scaffold MCP tool. Auto-appends _app / +-app to name (matching the MCP tool’s behaviour) so +Python and MCP callers see the same result for the same inputs.

+
+
Return type:
+

List[Path]

+
+
+
+ +
+
+scitex.app.validate(app_dir='.')[source]
+

Audit a SciTeX app for cloud-submission readiness.

+

Mirrors the app_validate MCP tool. Returns the list of +errors (empty when the app is ready).

+
+
Return type:
+

List[str]

+
+
+
+ +
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/audio.html b/src/scitex/_sphinx_html/source/api/audio.html new file mode 100644 index 000000000..7c35e7e0b --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/audio.html @@ -0,0 +1,735 @@ + + + + + + + + + Audio Module (stx.audio) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Audio Module (stx.audio)

+

Text-to-speech synthesis and audio file playback. Supports multiple +TTS backends with automatic fallback.

+
+

Quick Start

+
import scitex as stx
+
+# Text-to-speech
+stx.audio.speak("Analysis complete. 42 significant results found.")
+
+# Play an audio file
+stx.audio.play("notification.wav")
+
+
+
+
+

Key Functions

+
+

speak(text, backend=None, **kwargs)

+

Convert text to speech and play it through the default audio output. +If no backend is specified, the first available backend is used.

+
# Use default backend
+stx.audio.speak("Hello from SciTeX")
+
+# Specify a backend
+stx.audio.speak("Hello", backend="espeak")
+
+
+
+
+

play(path)

+

Play an audio file (WAV, MP3, etc.).

+
stx.audio.play("alert.wav")
+stx.audio.play("recording.mp3")
+
+
+
+
+

list_backends()

+

List available TTS backends on the current system.

+
backends = stx.audio.list_backends()
+# e.g., ['espeak', 'festival', 'pyttsx3']
+
+
+
+
+
+

Use Cases

+

Audible notifications are useful for long-running experiments:

+
import scitex as stx
+
+@stx.session
+def main(CONFIG=stx.INJECTED):
+    result = train_model()  # Takes hours
+    stx.audio.speak(f"Training done. Accuracy: {result.accuracy:.1%}")
+    return 0
+
+
+
+
+

API Reference

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/audit.html b/src/scitex/_sphinx_html/source/api/audit.html new file mode 100644 index 000000000..b70a5efc9 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/audit.html @@ -0,0 +1,705 @@ + + + + + + + + + audit Module (stx.audit) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

audit Module (stx.audit)

+

SciTeX Audit Module

+

Unified security scanning by orchestrating bandit (Python), shellcheck (shell), +pip-audit (deps), and GitHub alerts.

+
+
Usage:

from scitex_audit import audit

+

results = audit(“.”) +results = audit(“.”, checks=[“python”, “shell”])

+
+
+
+
+scitex.audit.audit(path='.', checks=None, output_file=None)[source]
+

Run security audit across multiple tools.

+
+
Parameters:
+
    +
  • path (str) – Directory to scan. Defaults to current directory.

  • +
  • checks (Optional[list[str]]) – Which checks to run. Options: “python”, “shell”, “deps”, “github”. +None means run all available checks.

  • +
  • output_file (Optional[str]) – If given, write JSON report to this path.

  • +
+
+
Returns:
+

Keys are check names, values have {status, findings, summary}.

+
+
Return type:
+

dict

+
+
+
+ +
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/benchmark.html b/src/scitex/_sphinx_html/source/api/benchmark.html new file mode 100644 index 000000000..1426179c0 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/benchmark.html @@ -0,0 +1,674 @@ + + + + + + + + + benchmark Module (stx.benchmark) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

benchmark Module (stx.benchmark)

+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/bridge.html b/src/scitex/_sphinx_html/source/api/bridge.html new file mode 100644 index 000000000..4345516bc --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/bridge.html @@ -0,0 +1,674 @@ + + + + + + + + + bridge Module (stx.bridge) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

bridge Module (stx.bridge)

+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/browser.html b/src/scitex/_sphinx_html/source/api/browser.html new file mode 100644 index 000000000..eff167ff1 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/browser.html @@ -0,0 +1,674 @@ + + + + + + + + + browser Module (stx.browser) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

browser Module (stx.browser)

+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/canvas.html b/src/scitex/_sphinx_html/source/api/canvas.html new file mode 100644 index 000000000..713e001ff --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/canvas.html @@ -0,0 +1,674 @@ + + + + + + + + + canvas Module (stx.canvas) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

canvas Module (stx.canvas)

+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/capture.html b/src/scitex/_sphinx_html/source/api/capture.html new file mode 100644 index 000000000..4d6fb384a --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/capture.html @@ -0,0 +1,738 @@ + + + + + + + + + Capture Module (stx.capture) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Capture Module (stx.capture)

+

Screenshot capture and screen monitoring utilities. Useful for +documenting GUI states, recording experiment progress, and +automated visual testing.

+
+

Quick Start

+
import scitex as stx
+
+# Take a screenshot
+img = stx.capture.snap()
+
+# Save it
+stx.io.save(img, "screenshot.png")
+
+
+
+
+

Key Functions

+
+

snap(region=None)

+

Capture a screenshot of the entire screen or a specific region.

+
# Full screen
+img = stx.capture.snap()
+
+# Specific region (x, y, width, height)
+img = stx.capture.snap(region=(0, 0, 800, 600))
+
+
+
+
+

monitor(interval=1.0, duration=None, output_dir=".")

+

Capture screenshots at regular intervals. Useful for monitoring +long-running GUI processes.

+
# Capture every 5 seconds for 1 minute
+stx.capture.monitor(interval=5.0, duration=60, output_dir="./captures")
+
+
+
+
+

crop(image, region)

+

Crop a captured image to a specific region.

+
img = stx.capture.snap()
+cropped = stx.capture.crop(img, region=(100, 100, 400, 300))
+stx.io.save(cropped, "cropped.png")
+
+
+
+
+
+

Use Cases

+

Documenting experiment results displayed in a GUI:

+
import scitex as stx
+
+@stx.session
+def main(CONFIG=stx.INJECTED):
+    run_experiment_gui()
+    img = stx.capture.snap()
+    stx.io.save(img, "final_state.png")
+    return 0
+
+
+
+
+

API Reference

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/clew.html b/src/scitex/_sphinx_html/source/api/clew.html new file mode 100644 index 000000000..6ff70ae92 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/clew.html @@ -0,0 +1,1281 @@ + + + + + + + + + Clew Module (stx.clew) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Clew Module (stx.clew)

+

Hash-based provenance tracking for reproducible science. Clew (Ariadne’s +thread) records file hashes during @stx.session runs and traces +dependency chains back to source.

+
+

How It Works

+
    +
  1. @stx.session starts a tracking session

  2. +
  3. stx.io.load() records input file hashes

  4. +
  5. stx.io.save() records output file hashes

  6. +
  7. Session close computes a combined hash of all inputs/outputs

  8. +
  9. Later, stx.clew can verify nothing has changed

  10. +
+
import scitex as stx
+
+# Automatic -- just use @stx.session + stx.io
+@stx.session
+def main():
+    data = stx.io.load("input.csv")      # Tracked as input
+    result = process(data)
+    stx.io.save(result, "output.png")     # Tracked as output
+    return 0
+
+# Verify later
+stx.clew.status()                         # Like git status
+stx.clew.run("session_id")                # Verify by hash
+stx.clew.chain("output.png")              # Trace to source
+
+
+
+
+

CLI Commands

+
scitex clew status                  # Show changed files
+scitex clew list                    # List all tracked runs
+scitex clew run <session_id>        # Verify a specific run
+scitex clew chain <file>            # Trace dependency chain
+scitex clew stats                   # Database statistics
+
+
+
+
+

Verification Levels

+
    +
  • CACHE – Hash comparison only (fast). Checks if files match stored hashes.

  • +
  • RERUN – Re-execute scripts and compare outputs (thorough). Catches logic errors.

  • +
+
# Fast: hash comparison
+result = stx.clew.run("session_id")
+
+# Thorough: re-execute and compare
+result = stx.clew.run("session_id", from_scratch=True)
+
+
+
+
+

Dependency Chains

+

Clew traces parent_session links to build a DAG from final output +back to original source:

+
chain = stx.clew.chain("final_figure.png")
+# Shows: source.py → intermediate.csv → analysis.py → final_figure.png
+
+# Visualize as Mermaid DAG
+stx.clew.mermaid("session_id")
+
+
+
+
+

Verification Statuses

+
    +
  • VERIFIED – Files match expected hashes

  • +
  • MISMATCH – Files differ from stored hashes

  • +
  • MISSING – Files no longer exist

  • +
  • UNKNOWN – No prior tracking data

  • +
+
+
+

Key Functions

+
    +
  • status() – Show changed items (like git status)

  • +
  • run(session_id) – Verify a specific run

  • +
  • chain(target_file) – Trace dependency chain

  • +
  • list_runs(limit, status) – List tracked runs

  • +
  • stats() – Database statistics

  • +
+
+
+

API Reference

+

scitex-clew — Hash-based verification for reproducible science.

+

Standalone package. Zero dependencies (pure stdlib + sqlite3). +When used with scitex, integration is automatic via @stx.session + stx.io.

+

Public API:

+
import scitex_clew as clew
+
+# Verification
+clew.status()                      # git-status-like overview
+clew.run(session_id)               # verify one run (hash check)
+clew.chain(target_file)            # trace file → source chain
+clew.dag(targets)                  # verify full DAG
+clew.rerun(target)                 # re-execute & compare (sandbox)
+clew.rerun_dag(targets)            # rerun full DAG in topo order
+clew.rerun_claims()                # rerun all claim-backing sessions
+clew.list_runs(limit=100)          # list tracked runs
+clew.stats()                       # database statistics
+
+# Claims
+clew.add_claim(...)                # register manuscript assertion
+clew.list_claims(...)              # list registered claims
+clew.verify_claim(...)             # verify a specific claim
+
+# Stamping
+clew.stamp(...)                    # create temporal proof
+clew.list_stamps(...)              # list stamps
+clew.check_stamp(...)              # verify a stamp
+
+# Hashing
+clew.hash_file(path)               # SHA256 of a file
+clew.hash_directory(path)          # SHA256 of all files in dir
+
+# Visualization
+clew.mermaid(...)                  # generate Mermaid DAG diagram
+
+# Examples
+clew.init_examples(dest)           # scaffold example pipeline
+
+# Session lifecycle hooks (invoked by @scitex.session)
+clew.on_session_start(session_id)  # open a tracked run
+clew.on_session_close(status=...)  # finalize run + combined hash
+
+
+
+
+scitex.clew.status()[source]
+

Get verification status summary (like git status).

+
+ +
+
+scitex.clew.run(session_id, from_scratch=False)[source]
+

Verify a specific run.

+
+
Parameters:
+
    +
  • session_id (str) – Session identifier

  • +
  • from_scratch (bool) – If True, re-execute the script and verify outputs (slow but thorough). +If False, only compare hashes (fast).

  • +
+
+
+
+ +
+
+scitex.clew.chain(target)[source]
+

Verify the dependency chain for a target file.

+
+ +
+
+scitex.clew.dag(targets=None, claims=False, strict=False)[source]
+

Verify the DAG for multiple targets or all claims.

+
+
Parameters:
+
    +
  • targets (list of str or Path, optional) – Target files to verify (mutually exclusive with claims).

  • +
  • claims (bool, optional) – If True, build the DAG from every registered claim.

  • +
  • strict (bool, optional) – If True (F2), return a failure-attribution dict with +failed_node / root_cause / invalidated_claims / +still_valid_claims instead of a DAGVerification.

  • +
+
+
+
+ +
+
+scitex.clew.rerun(target, timeout=300, cleanup=True)[source]
+

Re-execute a session in a sandbox and compare outputs.

+
+
Parameters:
+
    +
  • target (str or list[str]) – Session ID, script path, or artifact path.

  • +
  • timeout (int) – Maximum execution time in seconds (default: 300).

  • +
  • cleanup (bool) – Remove sandbox outputs after verification (default: True).

  • +
+
+
+
+ +
+
+scitex.clew.rerun_dag(targets=None, timeout=300, cleanup=True)[source]
+

Rerun-verify an entire DAG in topological order.

+

Each session is re-executed in a sandbox against its ORIGINAL stored +inputs (not freshly rerun outputs from upstream), then compared to +the original outputs.

+
+
Parameters:
+
    +
  • targets (list[str] | None) – Target output files whose upstream DAG should be rerun. +If None, all runs in the database are used and their output +files become the targets.

  • +
  • timeout (int) – Maximum execution time per session in seconds (default: 300).

  • +
  • cleanup (bool) – Whether to remove sandbox output directories after each rerun.

  • +
+
+
Returns:
+

Unified verification result for the entire DAG.

+
+
Return type:
+

DAGVerification

+
+
+
+ +
+
+scitex.clew.rerun_claims(file_path=None, claim_type=None, timeout=300, cleanup=True)[source]
+

Rerun-verify all sessions that produced files referenced by claims.

+

Collects unique source files from matching claims, then delegates +to rerun_dag with those files as targets.

+
+
Parameters:
+
    +
  • file_path (str | None) – Filter claims by manuscript file path.

  • +
  • claim_type (str | None) – Filter claims by type (statistic, figure, table, text, value).

  • +
  • timeout (int) – Maximum execution time per session in seconds (default: 300).

  • +
  • cleanup (bool) – Whether to remove sandbox output directories after each rerun.

  • +
+
+
Returns:
+

Unified verification result for the upstream DAG of all +source files referenced by the matching claims.

+
+
Return type:
+

DAGVerification

+
+
+
+ +
+
+scitex.clew.list_runs(limit=100, status=None)[source]
+

List tracked runs.

+
+ +
+
+scitex.clew.stats()[source]
+

Get database statistics.

+
+ +
+
+scitex.clew.add_claim(file_path, claim_type, line_number=None, claim_value=None, source_file=None, source_session=None)[source]
+

Register a claim linking a manuscript assertion to the verification chain.

+
+
Parameters:
+
    +
  • file_path (str) – Path to the manuscript file (e.g., paper.tex).

  • +
  • claim_type (str) – One of: statistic, figure, table, text, value.

  • +
  • line_number (Optional[int]) – Line number in the manuscript.

  • +
  • claim_value (Optional[str]) – The asserted value (e.g., “p = 0.003”).

  • +
  • source_file (Optional[str]) – Path to the source file that produced this claim.

  • +
  • source_session (Optional[str]) – Session ID that produced the source.

  • +
+
+
Returns:
+

The registered claim object.

+
+
Return type:
+

Claim

+
+
+
+ +
+
+scitex.clew.list_claims(file_path=None, claim_type=None, status=None, limit=100)[source]
+

List registered claims with optional filters.

+
+
Parameters:
+
    +
  • file_path (Optional[str]) – Filter by manuscript file path.

  • +
  • claim_type (Optional[str]) – Filter by claim type.

  • +
  • status (Optional[str]) – Filter by verification status.

  • +
  • limit (int) – Maximum number of claims to return.

  • +
+
+
Return type:
+

List[Claim]

+
+
+
+ +
+
+scitex.clew.verify_claim(claim_id_or_location)[source]
+

Verify a specific claim by checking its source against the verification chain.

+
+
Parameters:
+

claim_id_or_location (str) – Either a claim_id or a location string like “paper.tex:L42”.

+
+
Returns:
+

Verification result with claim details and chain status.

+
+
Return type:
+

Dict

+
+
+
+ +
+
+scitex.clew.export_claims_json(path=None, *, file_path_filter=None, read_only=True)[source]
+

Export every registered claim to a canonical JSON artifact.

+

The exported file is the single human-readable + machine-consumable +view of the claims table in db.sqlite. The DB remains the +source of truth; this JSON is a regenerable artifact.

+

Path resolution (mirrors scitex_clew._db._core._default_db_path()):

+
1. Explicit ``path`` argument.
+2. ``$SCITEX_CLEW_CLAIMS_JSON`` env var (escape hatch).
+3. ``<project_root>/.scitex/clew/runtime/claims.json``
+   (project root = nearest ancestor dir with ``.git`` or
+   ``pyproject.toml``; falls back to cwd if none found).
+
+
+
+
Parameters:
+
    +
  • path (Union[str, Path, None]) – Override the resolved path. Useful for tests / one-off dumps.

  • +
  • file_path_filter (Optional[str]) – When set, only claims registered against this manuscript file +path are exported. Default: every claim in the DB.

  • +
  • read_only (bool) – After writing, chmod 0o444 the file so accidental edits +fail loudly at the OS layer. Default True (the file IS +derived). Set False for tests that need to mutate the file.

  • +
+
+
Returns:
+

The path the artifact was written to (absolute).

+
+
Return type:
+

Path

+
+
+
+

Examples

+
>>> import scitex_clew as clew
+>>> clew.add_claim("paper.tex", "value", 42, "0.94", source_file="r.csv")
+>>> # claims.json now auto-exported under ./.scitex/clew/runtime/
+>>> clew.export_claims_json()  # idempotent — re-emit on demand
+PosixPath('.../.scitex/clew/runtime/claims.json')
+
+
+
+
+ +
+
+scitex.clew.register_intermediate(name, value, supports=None, session_id=None, claim_type='value')[source]
+

Register a computed intermediate as a Clew claim.

+

Use this from inside a @stx.session script (or from an agent loop) to +record any non-trivial intermediate value with explicit upstream support. +The claim becomes part of the DAG and can be queried via clew.chain, +clew.dag, or the MCP clew_chain / clew_dag tools.

+
+
Parameters:
+
    +
  • name (str) – Descriptive identifier (e.g. “acute_n_sig_pathways”). Avoid generic +names like “result_3” — the id is the only handle a future inspector +has on the value.

  • +
  • value (Any) – The computed result. Coerced to string for storage; the hash chain +sees repr(value) so types matter.

  • +
  • supports (Optional[List[str]]) – List of upstream claim ids or session ids that this value depends on. +Stored as JSON in the claim’s value field for retrieval. None means +no explicit upstream (use sparingly).

  • +
  • session_id (Optional[str]) – The session this value belongs to. If None, read from the +SCITEX_SESSION_ID env var that @stx.session sets at start.

  • +
  • claim_type (str) – One of statistic, figure, table, text, value. Defaults to +value since intermediates are usually scalar / categorical results.

  • +
+
+
Returns:
+

The registered claim object.

+
+
Return type:
+

Claim

+
+
Raises:
+

ValueError – If no session_id can be determined (env var unset and not passed).

+
+
+
+

Examples

+

Inside a @stx.session script:

+
>>> from scitex_clew import register_intermediate
+>>> n_sig = sum(1 for p in pathways if p.padj < 0.05)
+>>> register_intermediate(
+...     name="chronic_r2_n_sig_pathways",
+...     value=n_sig,
+...     supports=["chronic_r2_min_pvals", "reactome_pathways_v2024"],
+... )
+
+
+
+
+ +
+
+scitex.clew.stamp(backend='file', service_url=None, session_ids=None, output_dir=None)[source]
+

Record root hash with external timestamp.

+
+
Parameters:
+
    +
  • backend (str) – One of: file, rfc3161, zenodo.

  • +
  • service_url (Optional[str]) – URL for RFC 3161 TSA or Zenodo API.

  • +
  • session_ids (Optional[List[str]]) – Specific sessions to stamp. If None, stamps all successful runs.

  • +
  • output_dir (Optional[str]) – Directory for file-based stamps (default: <db_dir>/stamps, i.e. .scitex/clew/runtime/stamps/).

  • +
+
+
Returns:
+

The timestamp proof record.

+
+
Return type:
+

Stamp

+
+
+
+ +
+
+scitex.clew.list_stamps(limit=20)[source]
+

List all stamps.

+
+
Return type:
+

List[Stamp]

+
+
+
+ +
+
+scitex.clew.check_stamp(stamp_id=None)[source]
+

Verify a stamp against current verification state.

+
+
Parameters:
+

stamp_id (Optional[str]) – Specific stamp to check. If None, checks the latest stamp.

+
+
Returns:
+

{stamp, current_root_hash, matches, details}

+
+
Return type:
+

Dict

+
+
+
+ +
+
+scitex.clew.hash_file(path, algorithm='sha256', chunk_size=8192)[source]
+

Compute hash of a file.

+
+
Parameters:
+
    +
  • path (Union[str, Path]) – Path to the file to hash

  • +
  • algorithm (str) – Hash algorithm (default: sha256)

  • +
  • chunk_size (int) – Size of chunks to read (default: 8192)

  • +
+
+
Returns:
+

Hexadecimal hash string (first 32 characters)

+
+
Return type:
+

str

+
+
+
+

Examples

+
>>> hash_file("data.csv")
+'a1b2c3d4e5f6...'
+
+
+
+
+ +
+
+scitex.clew.hash_directory(path, pattern='*', recursive=True, algorithm='sha256')[source]
+

Compute hashes for all files in a directory.

+
+
Parameters:
+
    +
  • path (Union[str, Path]) – Directory path

  • +
  • pattern (str) – Glob pattern for files (default: “*”)

  • +
  • recursive (bool) – Whether to search recursively (default: True)

  • +
  • algorithm (str) – Hash algorithm (default: sha256)

  • +
+
+
Returns:
+

Mapping of relative paths to hashes

+
+
Return type:
+

Dict[str, str]

+
+
+
+

Examples

+
>>> hash_directory("./data/")
+{'input.csv': 'a1b2...', 'config.yaml': 'c3d4...'}
+
+
+
+
+ +
+
+scitex.clew.mermaid(session_id=None, target_file=None, target_files=None, claims=False, grouper=None, **kwargs)[source]
+

Generate a Mermaid DAG diagram.

+
+
Parameters:
+
    +
  • session_id (str, optional) – Start from this session.

  • +
  • target_file (str, optional) – Start from the session that produced this file.

  • +
  • target_files (list of str, optional) – Multiple target files (multi-target DAG).

  • +
  • claims (bool, optional) – If True, build DAG from all registered claims.

  • +
  • grouper (callable | dict | None, optional) – File grouping strategy. Callable or JSON/dict spec (see +scitex_clew.groupers.resolve_spec). If None, falls back to +.scitex/clew/config.yaml (key grouper) if present.

  • +
+
+
+
+ +
+
+scitex.clew.init_examples(dest, variant='sequential', *, find_examples_dir=<function _find_examples_dir>)[source]
+

Copy Clew example scripts to a destination directory.

+

Copies only the runnable scripts (.py, .sh) and README — not +the output directories. Users run 00_run_all.sh themselves +to generate outputs and populate the verification database.

+
+
Parameters:
+
    +
  • dest (str | Path) – Destination directory. Created if it does not exist. +Existing script files are overwritten.

  • +
  • variant (str) – Example variant: “sequential” (default) or “multi_parent”.

  • +
  • find_examples_dir (callable, optional) – Locator callable (variant: str) -> Optional[Path] used to +resolve the bundled examples source. Production callers should +not pass this; it is the canonical PA-306 §1 DI seam — tests +inject a hand-rolled fake that returns a tmp_path-rooted +directory or None.

  • +
+
+
Returns:
+

{"path": str, "files": list[str], "file_count": int, "variant": str}

+
+
Return type:
+

dict

+
+
Raises:
+
+
+
+
+ +
+
+scitex.clew.on_session_start(session_id, script_path=None, parent_session=None, verbose=False, metadata=None)[source]
+

Hook called when a session starts.

+
+
Parameters:
+
    +
  • session_id (str) – Unique session identifier

  • +
  • script_path (Optional[str]) – Path to the script being run

  • +
  • parent_session (Optional[str]) – Parent session ID for chain tracking

  • +
  • verbose (bool) – Whether to log status messages

  • +
  • metadata (Optional[dict]) – Additional metadata (e.g. notebook_path, cell_index)

  • +
+
+
Return type:
+

None

+
+
+
+ +
+
+scitex.clew.on_session_close(status='success', exit_code=0, verbose=False, register=None)[source]
+

Hook called when a session closes.

+
+
Parameters:
+
    +
  • status (str) – Final status (success, failed, error)

  • +
  • exit_code (int) – Exit code of the script

  • +
  • verbose (bool) – Whether to log status messages

  • +
  • register (Optional[bool]) – If True, register session hashes with remote Clew Registry. +If None, checks SCITEX_AUTO_REGISTER environment variable.

  • +
+
+
Return type:
+

None

+
+
+
+ +
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/cli.html b/src/scitex/_sphinx_html/source/api/cli.html new file mode 100644 index 000000000..48970167e --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/cli.html @@ -0,0 +1,737 @@ + + + + + + + + + cli Module (stx.cli) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

cli Module (stx.cli)

+

SciTeX CLI - Command-line interface for SciTeX platform

+

Provides unified interface for: +- Cloud operations (wraps tea for Gitea) +- Scholar operations (Django API) +- Code operations (Django API) +- Viz operations (Django API) +- Writer operations (Django API) +- Project operations (integrated workflows)

+
+
+scitex.cli.print_help_recursive(ctx, group)[source]
+

Print help for a group and all its subcommands.

+
+
Parameters:
+
    +
  • ctx – The click context

  • +
  • group (Group) – The click group to print help for

  • +
+
+
Return type:
+

None

+
+
+
+ +
+
+scitex.cli.format_python_signature(func, multiline=True, indent='  ')[source]
+

Format Python function signature with colors matching mcp list-tools.

+

Returns (name_colored, signature_colored)

+
+
Return type:
+

tuple

+
+
+
+ +
+
+scitex.cli.group_to_json(ctx, group)[source]
+

Output a group’s subcommands as Result JSON and exit.

+

Provides --json behavior for group-level commands, listing +available subcommands with their short help text.

+
+
Return type:
+

None

+
+
+
+ +
+
+scitex.cli.help_recursive_to_json(ctx, group)[source]
+

Output recursive help for a group and all subcommands as Result JSON.

+

Walks the entire command tree and outputs structured JSON with +command names, help text, options, and nested subcommands.

+
+
Return type:
+

None

+
+
+
+ +
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/cloud.html b/src/scitex/_sphinx_html/source/api/cloud.html new file mode 100644 index 000000000..fb755980f --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/cloud.html @@ -0,0 +1,778 @@ + + + + + + + + + Cloud Module (stx.cloud) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Cloud Module (stx.cloud)

+

Cloud platform integration for the SciTeX ecosystem. Provides +programmatic access to remote execution, file storage, job management, +and repository operations.

+
+

Overview

+

The cloud module connects local SciTeX workflows to the cloud platform, +enabling remote computation, shared storage, and collaborative +features. All operations are available through Python API, CLI, and +MCP tools.

+
+
+

Quick Start

+
import scitex as stx
+
+# Check cloud connection
+status = stx.cloud.status()
+
+# Submit a job
+job_id = stx.cloud.jobs.submit(script="train.py", resources={"gpu": 1})
+
+# Check job status
+stx.cloud.jobs.status(job_id)
+
+
+
+
+

Key Features

+
+

Job Management

+

Submit, monitor, and retrieve results from remote compute jobs.

+
# Submit a job
+job_id = stx.cloud.jobs.submit(
+    script="train.py",
+    resources={"gpu": 1, "memory": "16G"},
+)
+
+# Poll status
+status = stx.cloud.jobs.status(job_id)
+
+# List all jobs
+stx.cloud.jobs.list()
+
+# Cancel a running job
+stx.cloud.jobs.cancel(job_id)
+
+
+
+
+

File Storage

+

Upload and download files to/from cloud storage.

+
# Upload a file
+stx.cloud.files.upload("local_data.h5", remote="data/experiment.h5")
+
+# Download a file
+stx.cloud.files.download("data/experiment.h5", local="./downloaded.h5")
+
+# List remote files
+stx.cloud.files.list("data/")
+
+
+
+
+

Data API

+

CRUD operations on structured data stored in the cloud.

+
# Create a record
+stx.cloud.data.create(collection="results", data={"accuracy": 0.95})
+
+# Search records
+results = stx.cloud.data.search(collection="results", query={"accuracy": {"$gt": 0.9}})
+
+# List collections
+stx.cloud.data.list()
+
+
+
+
+

Repository Operations

+

Manage git repositories through the cloud API.

+
stx.cloud.repo.list()
+stx.cloud.repo.status("my-project")
+stx.cloud.repo.push("my-project")
+
+
+
+
+
+

CLI Access

+
# Job management
+scitex cloud jobs submit train.py --gpu 1
+scitex cloud jobs status JOB_ID
+scitex cloud jobs list
+
+# File operations
+scitex cloud files upload data.h5
+scitex cloud files list
+
+# Status
+scitex cloud status
+
+
+
+
+

API Reference

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/compat.html b/src/scitex/_sphinx_html/source/api/compat.html new file mode 100644 index 000000000..d4e845049 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/compat.html @@ -0,0 +1,702 @@ + + + + + + + + + compat Module (stx.compat) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

compat Module (stx.compat)

+

SciTeX Backward Compatibility Module.

+

This module provides aliases and wrappers for deprecated APIs. +Import from here to use old function names that delegate to new implementations.

+

Deprecation Timeline: +- v1.x: Old APIs work with deprecation warnings +- v2.x: Old APIs removed, use new APIs directly

+
+
+scitex.compat.deprecated(new_name, removal_version='2.0')[source]
+

Decorator to mark functions as deprecated.

+
+ +
+
+scitex.compat.notify(*args, **kwargs)[source]
+

Deprecated: Use scitex.notify.alert() instead.

+

In standalone mode, this only emits a deprecation warning. +The actual notification requires scitex.notify to be installed.

+
+ +
+
+async scitex.compat.notify_async(*args, **kwargs)[source]
+

Deprecated: Use scitex.notify.alert_async() instead.

+

In standalone mode, this only emits a deprecation warning. +The actual notification requires scitex.notify to be installed.

+
+ +
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/config.html b/src/scitex/_sphinx_html/source/api/config.html new file mode 100644 index 000000000..95a16240b --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/config.html @@ -0,0 +1,1403 @@ + + + + + + + + + Config Module (stx.config) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Config Module (stx.config)

+

YAML-based configuration with priority resolution and path management.

+
+

Priority Resolution

+

All config values follow the same precedence:

+
direct argument > config/YAML > environment variable > default
+
+
+
+
+

Quick Reference

+
import scitex as stx
+
+# Get global config (reads ~/.scitex/config.yaml)
+config = stx.config.get_config()
+
+# Resolve with precedence
+log_level = config.resolve("logging.level", default="INFO")
+# Checks: direct → YAML → SCITEX_LOGGING_LEVEL env → "INFO"
+
+# Access nested values
+config.get_nested("scholar", "crossref_email")
+
+# Path management
+paths = stx.config.get_paths()
+paths.scholar        # ~/.scitex/scholar
+paths.cache          # ~/.scitex/cache
+
+
+
+
+

YAML Configuration

+
# ~/.scitex/config.yaml (or ./config/*.yaml for projects)
+logging:
+  level: INFO
+
+scholar:
+  crossref_email: ${CROSSREF_EMAIL:-user@example.com}
+
+
+

Environment variable substitution with ${VAR:-default} syntax is +supported in YAML files.

+
+
+

Key Classes

+
    +
  • ScitexConfig – YAML-based config with env var substitution and precedence resolution

  • +
  • PriorityConfig – Dict-based config resolver for programmatic use

  • +
  • ScitexPaths – Centralized path manager (all paths derive from ~/.scitex/)

  • +
  • EnvVar – Environment variable definition dataclass

  • +
+
+
+

Path Management

+

ScitexPaths manages all SciTeX directories:

+
paths = stx.config.get_paths()
+paths.base       # ~/.scitex (SCITEX_DIR)
+paths.logs       # ~/.scitex/logs
+paths.cache      # ~/.scitex/cache
+paths.scholar    # ~/.scitex/scholar
+paths.rng        # ~/.scitex/rng
+paths.capture    # ~/.scitex/capture
+
+
+
+
+

Utility Functions

+
    +
  • get_config(path) – Get singleton ScitexConfig instance

  • +
  • get_paths(base_dir) – Get singleton ScitexPaths instance

  • +
  • load_yaml(path) – Load YAML with env var substitution

  • +
  • load_dotenv(path) – Load .env file

  • +
  • get_scitex_dir() – Get SCITEX_DIR with precedence

  • +
+
+
+

API Reference

+

scitex-config — configuration helpers (YAML + dotenv) — standalone.

+

Two distinct API surfaces:

+

Public (top-level scitex_config.*) — convention-free generic +primitives usable by any Python project:

+
    +
  • PriorityConfig — direct → config_dict → env → default cascade

  • +
  • ScitexConfig, get_config, load_yaml — YAML-based config

  • +
  • ScitexPaths, get_paths — centralized path manager

  • +
  • load_dotenv, get_scitex_dir — utilities

  • +
  • parse_src_file, load_env_from_path, load_scitex_env — bash-style +.src/.env parsing (canonical line/value parser; load_dotenv delegates)

  • +
+

SciTeX-ecosystem internals (scitex_config._ecosystem.*) — helpers +that embed SciTeX conventions (pkg-short naming, project-scope walk to +.git/, _skills/<pkg>/ layout, SCITEX_<MODULE>_* env prefix). +For scitex-* package authors only; not a stable public API:

+
from scitex_config._ecosystem import local_state, env_registry
+
+
+
+
Priority Order (same for PriorityConfig and ScitexConfig):

direct → config (YAML/dict) → env → default

+
+
Usage:

from scitex_config import ScitexConfig, ScitexPaths, get_config, get_paths

+

# YAML-based configuration (Scholar pattern) +config = get_config() +log_level = config.resolve(“logging.level”, default=”INFO”)

+

# Centralized path manager +paths = get_paths() +print(paths.logs) # ~/.scitex/logs +print(paths.cache) # ~/.scitex/cache

+

# Use resolve() pattern in modules +cache_dir = paths.resolve(“cache”, user_provided_path)

+
+
+
+
+class scitex.config.ScitexConfig(config_path=None, env_prefix='SCITEX_')[source]
+

YAML-based configuration manager for SciTeX.

+

Loads configuration from YAML files with environment variable substitution. +Values can be resolved with priority: direct → config → env → default.

+
+

Examples

+
>>> from scitex.config import ScitexConfig
+>>> config = ScitexConfig()
+>>> config.resolve("logging.level", default="INFO")
+'INFO'
+>>> config.get("debug.enabled")
+False
+
+
+
+
+
+__init__(config_path=None, env_prefix='SCITEX_')[source]
+

Initialize ScitexConfig.

+
+
Parameters:
+
    +
  • config_path (Union[str, Path, None]) – Path to custom YAML config file. If None, uses default.yaml.

  • +
  • env_prefix (str) – Prefix for environment variables (default: “SCITEX_”)

  • +
+
+
+
+ +
+
+get(key, default=None)[source]
+

Get value from config directly (no precedence resolution).

+

Supports dot notation for nested keys.

+
+
Parameters:
+
    +
  • key (str) – Configuration key (e.g., “logging.level” or “debug.enabled”)

  • +
  • default (Any) – Default value if key not found

  • +
+
+
Returns:
+

Configuration value

+
+
Return type:
+

Any

+
+
+
+ +
+
+resolve(key, direct_val=None, default=None, type=<class 'str'>)[source]
+

Resolve value with precedence: direct → config → env → default.

+

This follows the Scholar module’s CascadeConfig pattern where +YAML config takes higher priority than environment variables.

+
+
Parameters:
+
    +
  • key (str) – Configuration key (e.g., “logging.level”)

  • +
  • direct_val (Any) – Direct value (highest precedence)

  • +
  • default (Any) – Default value (lowest precedence)

  • +
  • type (Type) – Type conversion (str, int, float, bool, list)

  • +
+
+
Returns:
+

Resolved value

+
+
Return type:
+

Any

+
+
+
+ +
+
+get_nested(*keys, default=None)[source]
+

Get nested value from original config structure.

+
+
Parameters:
+
    +
  • *keys (str) – Keys to traverse (e.g., “browser”, “screenshots_dir”)

  • +
  • default (Any) – Default value if not found

  • +
+
+
Returns:
+

Nested value

+
+
Return type:
+

Any

+
+
+
+ +
+
+property config_path: Path
+

Get the path to the loaded config file.

+
+ +
+
+property raw: dict
+

Get raw configuration data (original nested structure).

+
+ +
+
+property flat: dict
+

Get flattened configuration data.

+
+ +
+
+print()[source]
+

Print configuration resolution log.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+scitex.config.get_config(config_path=None)[source]
+

Get ScitexConfig instance.

+
+
Parameters:
+

config_path (Union[str, Path, None]) – Path to custom config. If None, returns cached default instance.

+
+
Returns:
+

Configuration instance

+
+
Return type:
+

ScitexConfig

+
+
+
+ +
+
+scitex.config.load_yaml(path)[source]
+

Load YAML file with environment variable substitution.

+

Supports ${VAR:-default} syntax for environment variable expansion.

+
+
Parameters:
+

path (Path) – Path to YAML file

+
+
Returns:
+

Parsed YAML with environment variables substituted

+
+
Return type:
+

dict

+
+
+
+ +
+
+class scitex.config.ScitexPaths(base_dir=None)[source]
+

Centralized path manager for SciTeX directories.

+

All paths are derived from SCITEX_DIR (default: ~/.scitex). +Priority: direct_val → SCITEX_DIR env → .env file → default

+
+
Directory Structure:

$SCITEX_DIR/ +├── browser/ # Browser profiles and data +│ ├── screenshots/ # Browser debugging screenshots +│ ├── sessions/ # Shared browser sessions +│ └── persistent/ # Persistent browser profiles +├── cache/ # General cache +│ └── functions/ # Function cache (joblib) +├── capture/ # Screen captures +├── impact_factor_cache/ # Impact factor data cache +├── logs/ # Log files +├── openathens_cache/ # OpenAthens auth cache +├── rng/ # Random number generator state +├── scholar/ # Scholar module data +│ ├── cache/ # Scholar-specific cache +│ └── library/ # PDF library +├── screenshots/ # General screenshots +├── test_monitor/ # Test monitoring screenshots +└── writer/ # Writer module data

+
+
+
+
+__init__(base_dir=None)[source]
+

Initialize ScitexPaths.

+
+
Parameters:
+

base_dir (Optional[str]) – Explicit base directory. If None, uses SCITEX_DIR env var +or falls back to ~/.scitex.

+
+
+
+ +
+
+property base: Path
+

Base SciTeX directory ($SCITEX_DIR or ~/.scitex).

+
+ +
+
+property logs: Path
+

Log files directory.

+
+ +
+
+property cache: Path
+

General cache directory.

+
+ +
+
+property capture: Path
+

Screen capture directory.

+
+ +
+
+property screenshots: Path
+

General screenshots directory.

+
+ +
+
+property rng: Path
+

Random number generator state directory.

+
+ +
+
+property browser: Path
+

Browser module base directory.

+
+ +
+
+property browser_screenshots: Path
+

Browser debugging screenshots.

+
+ +
+
+property browser_sessions: Path
+

Shared browser sessions.

+
+ +
+
+property browser_persistent: Path
+

Persistent browser profiles.

+
+ +
+
+property test_monitor: Path
+

Test monitoring screenshots directory.

+
+ +
+
+property function_cache: Path
+

Function cache (joblib memory).

+
+ +
+
+property impact_factor_cache: Path
+

Impact factor data cache.

+
+ +
+
+property openathens_cache: Path
+

OpenAthens authentication cache.

+
+ +
+
+property scholar: Path
+

Scholar module base directory.

+
+ +
+
+property scholar_cache: Path
+

Scholar-specific cache directory.

+
+ +
+
+property scholar_library: Path
+

Scholar PDF library directory.

+
+ +
+
+property writer: Path
+

Writer module directory.

+
+ +
+
+resolve(path_name, direct_val=None)[source]
+

Resolve a path with priority: direct_val → default from SCITEX_DIR.

+

This is the recommended method for modules that accept optional path +parameters. It follows the same pattern as PriorityConfig.resolve().

+
+
Parameters:
+
    +
  • path_name (str) – Name of the path property (e.g., “cache”, “logs”, “scholar_library”)

  • +
  • direct_val (Union[str, Path, None]) – Direct value (highest precedence). If None, uses default.

  • +
+
+
Returns:
+

Resolved path

+
+
Return type:
+

Path

+
+
+
+

Examples

+
>>> paths = ScitexPaths()
+>>> # User didn't provide path -> use default
+>>> cache_dir = paths.resolve("cache", None)
+>>> # User provided custom path -> use it
+>>> cache_dir = paths.resolve("cache", "/custom/cache")
+
+
+

Usage in modules: +>>> class MyModule: +… def __init__(self, cache_dir=None): +… self.cache_dir = get_paths().resolve(“cache”, cache_dir)

+
+
+ +
+
+ensure_dir(path)[source]
+

Ensure directory exists, creating if necessary.

+
+
Parameters:
+

path (Path) – Directory path to ensure exists.

+
+
Returns:
+

The same path, guaranteed to exist.

+
+
Return type:
+

Path

+
+
+
+ +
+
+ensure_all()[source]
+

Create all standard directories.

+
+
Return type:
+

None

+
+
+
+ +
+
+list_all()[source]
+

List all configured paths.

+
+
Returns:
+

Dictionary of path names to Path objects.

+
+
Return type:
+

dict

+
+
+
+ +
+ +
+
+scitex.config.get_paths(base_dir=None)[source]
+

Get ScitexPaths instance.

+
+
Parameters:
+

base_dir (Optional[str]) – Explicit base directory. If None, returns cached default instance.

+
+
Returns:
+

Path manager instance.

+
+
Return type:
+

ScitexPaths

+
+
+
+ +
+
+class scitex.config.PriorityConfig(config_dict=None, env_prefix='', auto_uppercase=True)[source]
+

Universal config resolver with precedence: direct → config_dict → env → default

+

Config dict (from YAML or passed dict) takes priority over env variables. +This follows the Scholar module’s CascadeConfig pattern.

+
+

Examples

+
>>> from scitex_config import PriorityConfig
+>>> config = PriorityConfig(config_dict={"port": 3000}, env_prefix="SCITEX_")
+>>> port = config.resolve("port", None, default=8000, type=int)
+3000  # from config_dict (highest after direct)
+>>> # With env: SCITEX_PORT=5000 python script.py
+>>> port = config.resolve("port", None, default=8000, type=int)
+3000  # config_dict takes precedence over env
+>>> port = config.resolve("port", 9000, default=8000, type=int)
+9000  # direct value takes highest precedence
+
+
+
+
+
+SENSITIVE_EXPRESSIONS = ['API', 'PASSWORD', 'SECRET', 'TOKEN', 'KEY', 'PASS', 'AUTH', 'CREDENTIAL', 'PRIVATE', 'CERT']
+
+ +
+
+__init__(config_dict=None, env_prefix='', auto_uppercase=True)[source]
+

Initialize PriorityConfig.

+
+
Parameters:
+
    +
  • config_dict (Optional[Dict[str, Any]]) – Dictionary with configuration values

  • +
  • env_prefix (str) – Prefix for environment variables (e.g., “SCITEX_”)

  • +
  • auto_uppercase (bool) – Whether to uppercase keys for env lookup

  • +
+
+
+
+ +
+
+resolution_log: List[Dict[str, Any]]
+
+ +
+
+get(key)[source]
+

Get value from config dict only.

+
+
Return type:
+

Any

+
+
+
+ +
+
+resolve(key, direct_val=None, default=None, type=<class 'str'>, mask=None)[source]
+

Get value with precedence hierarchy.

+

Precedence: direct → config_dict → env → default

+

This follows the Scholar module’s CascadeConfig pattern where +config dict takes higher priority than environment variables.

+
+
Parameters:
+
    +
  • key (str) – Configuration key to resolve

  • +
  • direct_val (Any) – Direct value (highest precedence)

  • +
  • default (Any) – Default value if not found elsewhere

  • +
  • type (Type) – Type conversion (str, int, float, bool, list)

  • +
  • mask (Optional[bool]) – Override automatic masking of sensitive values

  • +
+
+
Returns:
+

Resolved configuration value

+
+
Return type:
+

Any

+
+
+
+ +
+
+print_resolutions()[source]
+

Print how each config was resolved.

+
+
Return type:
+

None

+
+
+
+ +
+
+clear_log()[source]
+

Clear resolution log.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+scitex.config.get_scitex_dir(direct_val=None)[source]
+

Get SCITEX_DIR with priority: direct → env → default.

+

This is a convenience function for the most common use case.

+
+
Parameters:
+

direct_val (Optional[str]) – Direct value (highest precedence)

+
+
Returns:
+

Resolved SCITEX_DIR path

+
+
Return type:
+

Path

+
+
+
+ +
+
+scitex.config.load_dotenv(dotenv_path=None, *, walk_up=False, stop_at=None)[source]
+

Load environment variables from .env file(s).

+
+
Default behavior (walk_up=False, backward compatible):

Searches for .env file in the following order, loading the first match: +1. Explicit dotenv_path if provided +2. Current working directory (cwd/.env) +3. User home directory ($HOME/.env)

+
+
Parent-walking behavior (walk_up=True, opt-in):

Walks parent directories starting from cwd, looking for .env +at each level. Stops when reaching stop_at (or $HOME if not +given) or the filesystem root. All .env files found are loaded, +with the most-distant parent loaded first so that closer-to-cwd values +take precedence (closer .env wins). An existing process env var is +never overridden by any .env (process env > closest .env > … > root .env).

+

Note: walk_up=True is ignored if dotenv_path is explicitly given.

+
+
+
+
Parameters:
+
    +
  • dotenv_path (Optional[str]) – Path to .env file. If None, searches default locations.

  • +
  • walk_up (bool) – If True (and dotenv_path not given), walk parent dirs from cwd. +Default False for backward compatibility — new callers should pass True.

  • +
  • stop_at (Union[str, Path, None]) – Directory at which to stop the upward walk (inclusive — its .env +is considered). If None, stops at $HOME (or filesystem root if +$HOME is not a parent of cwd). Only used when walk_up=True.

  • +
+
+
Returns:
+

True if at least one .env file was found and loaded, False otherwise.

+
+
Return type:
+

bool

+
+
+
+ +
+
+scitex.config.parse_src_file(filepath)[source]
+

Parse a bash-compatible .src/.env file and extract env variables.

+
+
Parameters:
+

filepath (Path) – Path to the file.

+
+
Returns:
+

Dictionary of variable names to values.

+
+
Return type:
+

Dict[str, str]

+
+
+
+ +
+
+scitex.config.load_env_from_path(path)[source]
+

Load environment variables from a file or directory.

+
+
Parameters:
+

path (str) – Path to a .src file or directory containing *.src files.

+
+
Returns:
+

All loaded environment variables.

+
+
Return type:
+

Dict[str, str]

+
+
+
+ +
+
+scitex.config.load_scitex_env()[source]
+

Load environment variables from $SCITEX_ENV_SRC if set.

+

This function should be called early in the MCP server startup.

+
+
Returns:
+

Number of environment variables loaded.

+
+
Return type:
+

int

+
+
+
+ +
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/container.html b/src/scitex/_sphinx_html/source/api/container.html new file mode 100644 index 000000000..01987d762 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/container.html @@ -0,0 +1,674 @@ + + + + + + + + + container Module (stx.container) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

container Module (stx.container)

+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/context.html b/src/scitex/_sphinx_html/source/api/context.html new file mode 100644 index 000000000..e2a2b5c1b --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/context.html @@ -0,0 +1,856 @@ + + + + + + + + + context Module (stx.context) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

context Module (stx.context)

+

scitex-context — execution-context detection (script vs notebook vs IPython) + output suppression.

+
+
+scitex.context.detect_environment()[source]
+

Detect the current execution environment.

+
+
Returns:
+

One of: ‘script’, ‘jupyter’, ‘ipython’, ‘interactive’, ‘unknown’

+
+
Return type:
+

Literal['script', 'jupyter', 'ipython', 'interactive', 'unknown']

+
+
+
+

Examples

+
>>> env = detect_environment()
+>>> print(f"Running in: {env}")
+Running in: script
+
+
+
+
+ +
+
+scitex.context.get_notebook_directory()[source]
+

Get the directory containing the current notebook.

+
+
Returns:
+

Directory path, or None if not in a notebook

+
+
Return type:
+

Optional[str]

+
+
+
+ +
+
+scitex.context.get_notebook_info_simple()[source]
+

Simple method to get notebook info using current working directory.

+
+
Returns:
+

(notebook_name, notebook_directory)

+
+
Return type:
+

tuple[Optional[str], Optional[str]]

+
+
+
+ +
+
+scitex.context.get_notebook_name()[source]
+

Get just the name of the current notebook (without path).

+
+
Returns:
+

Notebook filename, or None if not in a notebook

+
+
Return type:
+

Optional[str]

+
+
+
+

Examples

+
>>> name = get_notebook_name()
+>>> if name:
+...     print(f"Notebook: {name}")
+
+
+
+
+ +
+
+scitex.context.get_notebook_path()[source]
+

Get the full path of the current Jupyter notebook.

+
+
Returns:
+

Full path to the notebook file, or None if not in a notebook

+
+
Return type:
+

Optional[str]

+
+
+
+

Examples

+
>>> path = get_notebook_path()
+>>> if path:
+...     print(f"Running in notebook: {path}")
+
+
+
+
+ +
+
+scitex.context.get_output_directory(specified_path, env_type=None)[source]
+

Get the appropriate output directory based on environment.

+
+
Parameters:
+
    +
  • specified_path (str) – The path specified by the user

  • +
  • env_type (Literal['script', 'jupyter', 'ipython', 'interactive', 'unknown']) – Override environment detection

  • +
+
+
Returns:
+

(output_directory, should_use_temp)

+
+
Return type:
+

Tuple[str, bool]

+
+
+
+

Examples

+
>>> output_dir, use_temp = get_output_directory("data.csv")
+>>> print(f"Save to: {output_dir}, Temp: {use_temp}")
+Save to: ./script_out/data.csv, Temp: False
+
+
+
+
+ +
+
+scitex.context.is_ipython()[source]
+

Check if running in IPython (console or notebook).

+
+
Return type:
+

bool

+
+
+
+ +
+
+scitex.context.is_notebook()[source]
+

Check if running in Jupyter notebook.

+
+
Return type:
+

bool

+
+
+
+ +
+
+scitex.context.is_script()[source]
+

Check if running as a script.

+
+
Return type:
+

bool

+
+
+
+ +
+
+scitex.context.quiet(suppress=True)
+

A context manager that suppresses stdout and stderr.

+
+

Example

+
+
with suppress_output():

print(“This will not be printed to the console.”)

+
+
+
+
+ +
+
+scitex.context.suppress_output(suppress=True)[source]
+

A context manager that suppresses stdout and stderr.

+
+

Example

+
+
with suppress_output():

print(“This will not be printed to the console.”)

+
+
+
+
+ +
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/cv.html b/src/scitex/_sphinx_html/source/api/cv.html new file mode 100644 index 000000000..27dda161c --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/cv.html @@ -0,0 +1,1147 @@ + + + + + + + + + cv Module (stx.cv) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

cv Module (stx.cv)

+

scitex-cv — small cv2-based image utilities (standalone).

+

Provides reusable cv2-based utilities for image processing: +- I/O: load, save, color conversions +- Transform: resize, rotate, flip, crop, pad +- Filters: blur, sharpen, edge detection, threshold, denoise +- Draw: rectangle, circle, line, text, polylines, arrow

+
+

Example

+
>>> import scitex.cv as cv
+>>> # Load and process an image
+>>> img = cv.load("input.png")
+>>> img = cv.resize(img, scale=0.5)
+>>> img = cv.blur(img, ksize=5)
+>>> edges = cv.edge_detect(img, method="canny")
+>>> cv.save(edges, "edges.png")
+
+
+
+
+
+scitex.cv.load(path, color=True, alpha=False)[source]
+

Load an image from file.

+
+
Parameters:
+
    +
  • path (Union[str, Path]) – Image file path.

  • +
  • color (bool) – If True, load as color (BGR). If False, load as grayscale.

  • +
  • alpha (bool) – If True, preserve alpha channel (BGRA).

  • +
+
+
Returns:
+

Image array in BGR or grayscale format.

+
+
Return type:
+

ndarray

+
+
+
+ +
+
+scitex.cv.save(img, path, quality=95)[source]
+

Save an image to file.

+
+
Parameters:
+
    +
  • img (ndarray) – Image array (BGR or grayscale).

  • +
  • path (Union[str, Path]) – Output file path.

  • +
  • quality (int) – JPEG quality (0-100) or PNG compression (0-9).

  • +
+
+
Returns:
+

Path to saved file.

+
+
Return type:
+

Path

+
+
+
+ +
+
+scitex.cv.to_rgb(img)[source]
+

Convert BGR image to RGB.

+
+
Parameters:
+

img (ndarray) – BGR image.

+
+
Returns:
+

RGB image.

+
+
Return type:
+

ndarray

+
+
+
+ +
+
+scitex.cv.to_bgr(img)[source]
+

Convert RGB image to BGR.

+
+
Parameters:
+

img (ndarray) – RGB image.

+
+
Returns:
+

BGR image.

+
+
Return type:
+

ndarray

+
+
+
+ +
+
+scitex.cv.to_gray(img)[source]
+

Convert image to grayscale.

+
+
Parameters:
+

img (ndarray) – Color image (BGR or RGB).

+
+
Returns:
+

Grayscale image.

+
+
Return type:
+

ndarray

+
+
+
+ +
+
+scitex.cv.resize(img, size=None, scale=None, interpolation='linear')[source]
+

Resize an image.

+
+
Parameters:
+
    +
  • img (ndarray) – Input image.

  • +
  • size (Optional[Tuple[int, int]]) – Target size as (width, height).

  • +
  • scale (Optional[float]) – Scale factor (alternative to size).

  • +
  • interpolation (str) – Interpolation method: ‘nearest’, ‘linear’, ‘cubic’, ‘area’, ‘lanczos’.

  • +
+
+
Returns:
+

Resized image.

+
+
Return type:
+

ndarray

+
+
+
+ +
+
+scitex.cv.rotate(img, angle, center=None, scale=1.0)[source]
+

Rotate an image.

+
+
Parameters:
+
    +
  • img (ndarray) – Input image.

  • +
  • angle (float) – Rotation angle in degrees (counter-clockwise).

  • +
  • center (Optional[Tuple[int, int]]) – Rotation center. Defaults to image center.

  • +
  • scale (float) – Scale factor.

  • +
+
+
Returns:
+

Rotated image.

+
+
Return type:
+

ndarray

+
+
+
+ +
+
+scitex.cv.flip(img, direction='horizontal')[source]
+

Flip an image.

+
+
Parameters:
+
    +
  • img (ndarray) – Input image.

  • +
  • direction (str) – Flip direction: ‘horizontal’, ‘vertical’, or ‘both’.

  • +
+
+
Returns:
+

Flipped image.

+
+
Return type:
+

ndarray

+
+
+
+ +
+
+scitex.cv.crop(img, x, y, width, height)[source]
+

Crop a region from an image.

+
+
Parameters:
+
    +
  • img (ndarray) – Input image.

  • +
  • x (int) – Top-left corner coordinates.

  • +
  • y (int) – Top-left corner coordinates.

  • +
  • width (int) – Crop dimensions.

  • +
  • height (int) – Crop dimensions.

  • +
+
+
Returns:
+

Cropped image.

+
+
Return type:
+

ndarray

+
+
+
+ +
+
+scitex.cv.pad(img, top=0, bottom=0, left=0, right=0, color=0, mode='constant')[source]
+

Pad an image.

+
+
Parameters:
+
    +
  • img (ndarray) – Input image.

  • +
  • top (int) – Padding sizes.

  • +
  • bottom (int) – Padding sizes.

  • +
  • left (int) – Padding sizes.

  • +
  • right (int) – Padding sizes.

  • +
  • color (Union[int, Tuple[int, ...]]) – Padding color for constant mode.

  • +
  • mode (str) – Padding mode: ‘constant’, ‘reflect’, ‘replicate’.

  • +
+
+
Returns:
+

Padded image.

+
+
Return type:
+

ndarray

+
+
+
+ +
+
+scitex.cv.blur(img, ksize=5, method='gaussian')[source]
+

Apply blur to an image.

+
+
Parameters:
+
    +
  • img (ndarray) – Input image.

  • +
  • ksize (int) – Kernel size (must be odd).

  • +
  • method (str) – Blur method: ‘gaussian’, ‘median’, ‘box’, ‘bilateral’.

  • +
+
+
Returns:
+

Blurred image.

+
+
Return type:
+

ndarray

+
+
+
+ +
+
+scitex.cv.sharpen(img, strength=1.0)[source]
+

Sharpen an image.

+
+
Parameters:
+
    +
  • img (ndarray) – Input image.

  • +
  • strength (float) – Sharpening strength.

  • +
+
+
Returns:
+

Sharpened image.

+
+
Return type:
+

ndarray

+
+
+
+ +
+
+scitex.cv.edge_detect(img, method='canny', low=50, high=150)[source]
+

Detect edges in an image.

+
+
Parameters:
+
    +
  • img (ndarray) – Input image.

  • +
  • method (str) – Edge detection method: ‘canny’, ‘sobel’, ‘laplacian’.

  • +
  • low (int) – Thresholds for Canny detector.

  • +
  • high (int) – Thresholds for Canny detector.

  • +
+
+
Returns:
+

Edge image.

+
+
Return type:
+

ndarray

+
+
+
+ +
+
+scitex.cv.threshold(img, thresh=127, maxval=255, method='binary')[source]
+

Apply thresholding to an image.

+
+
Parameters:
+
    +
  • img (ndarray) – Input image.

  • +
  • thresh (int) – Threshold value.

  • +
  • maxval (int) – Maximum value.

  • +
  • method (str) – Threshold method: ‘binary’, ‘binary_inv’, ‘trunc’, ‘tozero’, +‘tozero_inv’, ‘otsu’, ‘adaptive_mean’, ‘adaptive_gaussian’.

  • +
+
+
Returns:
+

Thresholded image.

+
+
Return type:
+

ndarray

+
+
+
+ +
+
+scitex.cv.denoise(img, strength=10, method='fastNl')[source]
+

Denoise an image.

+
+
Parameters:
+
    +
  • img (ndarray) – Input image.

  • +
  • strength (int) – Denoising strength.

  • +
  • method (str) – Denoising method: ‘fastNl’, ‘bilateral’.

  • +
+
+
Returns:
+

Denoised image.

+
+
Return type:
+

ndarray

+
+
+
+ +
+
+scitex.cv.rectangle(img, pt1, pt2, color=(0, 255, 0), thickness=2, filled=False)[source]
+

Draw a rectangle on an image.

+
+
Parameters:
+
    +
  • img (ndarray) – Input image (modified in place).

  • +
  • pt1 (Tuple[int, int]) – Top-left corner (x, y).

  • +
  • pt2 (Tuple[int, int]) – Bottom-right corner (x, y).

  • +
  • color (Tuple[int, int, int]) – BGR color.

  • +
  • thickness (int) – Line thickness (-1 for filled).

  • +
  • filled (bool) – If True, fill the rectangle.

  • +
+
+
Returns:
+

Image with rectangle.

+
+
Return type:
+

ndarray

+
+
+
+ +
+
+scitex.cv.circle(img, center, radius, color=(0, 255, 0), thickness=2, filled=False)[source]
+

Draw a circle on an image.

+
+
Parameters:
+
    +
  • img (ndarray) – Input image (modified in place).

  • +
  • center (Tuple[int, int]) – Center coordinates (x, y).

  • +
  • radius (int) – Circle radius.

  • +
  • color (Tuple[int, int, int]) – BGR color.

  • +
  • thickness (int) – Line thickness.

  • +
  • filled (bool) – If True, fill the circle.

  • +
+
+
Returns:
+

Image with circle.

+
+
Return type:
+

ndarray

+
+
+
+ +
+
+scitex.cv.line(img, pt1, pt2, color=(0, 255, 0), thickness=2)[source]
+

Draw a line on an image.

+
+
Parameters:
+
+
+
Returns:
+

Image with line.

+
+
Return type:
+

ndarray

+
+
+
+ +
+
+scitex.cv.text(img, text, position, color=(255, 255, 255), scale=1.0, thickness=2, font='simplex')[source]
+

Draw text on an image.

+
+
Parameters:
+
    +
  • img (ndarray) – Input image (modified in place).

  • +
  • text (str) – Text to draw.

  • +
  • position (Tuple[int, int]) – Bottom-left corner of text (x, y).

  • +
  • color (Tuple[int, int, int]) – BGR color.

  • +
  • scale (float) – Font scale.

  • +
  • thickness (int) – Text thickness.

  • +
  • font (str) – Font type: ‘simplex’, ‘plain’, ‘duplex’, ‘complex’, ‘triplex’.

  • +
+
+
Returns:
+

Image with text.

+
+
Return type:
+

ndarray

+
+
+
+ +
+
+scitex.cv.polylines(img, points, closed=True, color=(0, 255, 0), thickness=2)[source]
+

Draw polylines on an image.

+
+
Parameters:
+
    +
  • img (ndarray) – Input image (modified in place).

  • +
  • points (ndarray) – Array of points with shape (N, 1, 2) or (N, 2).

  • +
  • closed (bool) – Whether to close the polyline.

  • +
  • color (Tuple[int, int, int]) – BGR color.

  • +
  • thickness (int) – Line thickness.

  • +
+
+
Returns:
+

Image with polylines.

+
+
Return type:
+

ndarray

+
+
+
+ +
+
+scitex.cv.arrow(img, pt1, pt2, color=(0, 255, 0), thickness=2, tip_length=0.1)[source]
+

Draw an arrowed line on an image.

+
+
Parameters:
+
    +
  • img (ndarray) – Input image (modified in place).

  • +
  • pt1 (Tuple[int, int]) – Start point (x, y).

  • +
  • pt2 (Tuple[int, int]) – End point (arrow tip) (x, y).

  • +
  • color (Tuple[int, int, int]) – BGR color.

  • +
  • thickness (int) – Line thickness.

  • +
  • tip_length (float) – Arrow tip length as fraction of line length.

  • +
+
+
Returns:
+

Image with arrow.

+
+
Return type:
+

ndarray

+
+
+
+ +
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/dataset.html b/src/scitex/_sphinx_html/source/api/dataset.html new file mode 100644 index 000000000..375d1a358 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/dataset.html @@ -0,0 +1,790 @@ + + + + + + + + + Dataset Module (stx.dataset) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Dataset Module (stx.dataset)

+

Unified access to public scientific datasets from major neuroscience +and biomedical repositories. Search, browse, and fetch datasets +without learning each repository’s API.

+
+

Supported Sources

+ ++++ + + + + + + + + + + + + + + + + +

Source

Description

DANDI

Neurophysiology data (NWB format) – electrophysiology, calcium imaging

OpenNeuro

Brain imaging data (BIDS format) – fMRI, EEG, MEG, PET

PhysioNet

Physiological signal data – ECG, EEG, clinical waveforms

+
+
+

Quick Start

+
import scitex as stx
+
+# Search across all sources
+results = stx.dataset.search("epilepsy EEG")
+
+# Fetch a specific dataset
+data = stx.dataset.fetch("dandi", dandiset_id="000003")
+
+# List available sources
+sources = stx.dataset.list_sources()
+# ['dandi', 'openneuro', 'physionet']
+
+
+
+
+

Key Functions

+
+

search(query, source=None)

+

Search for datasets across one or all supported repositories.

+
# Search all sources
+results = stx.dataset.search("motor imagery BCI")
+
+# Search a specific source
+results = stx.dataset.search("sleep staging", source="physionet")
+
+for r in results:
+    print(f"{r.source}: {r.title} ({r.id})")
+
+
+
+
+

fetch(source, **kwargs)

+

Download or stream a dataset from a specific source.

+
# Fetch from DANDI
+data = stx.dataset.fetch("dandi", dandiset_id="000003")
+
+# Fetch from OpenNeuro
+data = stx.dataset.fetch("openneuro", dataset_id="ds000117")
+
+# Fetch from PhysioNet
+data = stx.dataset.fetch("physionet", database="mimic-iv", version="2.0")
+
+
+
+
+

list_sources()

+

List all available dataset sources and their status.

+
sources = stx.dataset.list_sources()
+# ['dandi', 'openneuro', 'physionet']
+
+
+
+
+
+

Local Database

+

Build a local search index for faster repeated queries:

+
# Build/update the local database
+stx.dataset.db_build(sources=["dandi", "openneuro"])
+
+# Search the local index (fast, offline)
+results = stx.dataset.db_search("resting state fMRI")
+
+# Get database statistics
+stx.dataset.db_stats()
+
+
+
+
+

CLI Access

+
# Search datasets
+scitex dataset search "epilepsy EEG"
+
+# Fetch a dataset
+scitex dataset fetch dandi --dandiset-id 000003
+
+# List sources
+scitex dataset list-sources
+
+# Build local database
+scitex dataset db-build
+
+
+
+
+

API Reference

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/datetime.html b/src/scitex/_sphinx_html/source/api/datetime.html new file mode 100644 index 000000000..4594c7642 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/datetime.html @@ -0,0 +1,674 @@ + + + + + + + + + datetime Module (stx.datetime) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

datetime Module (stx.datetime)

+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/db.html b/src/scitex/_sphinx_html/source/api/db.html new file mode 100644 index 000000000..9e92db375 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/db.html @@ -0,0 +1,674 @@ + + + + + + + + + db Module (stx.db) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

db Module (stx.db)

+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/decorators.html b/src/scitex/_sphinx_html/source/api/decorators.html new file mode 100644 index 000000000..10405d4df --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/decorators.html @@ -0,0 +1,749 @@ + + + + + + + + + Decorators Module (stx.decorators) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Decorators Module (stx.decorators)

+

Function decorators for type conversion, batching, caching, and more.

+
+

Quick Reference

+
from scitex.decorators import torch_fn, batch_fn, cache_disk, timeout
+
+@torch_fn
+def process(x):
+    """Auto-converts inputs to torch tensors, outputs back."""
+    return x * 2
+
+@batch_fn
+def predict(data, batch_size=32):
+    """Process data in batches with progress bar."""
+    return model(data)
+
+@cache_disk
+def expensive_computation(params):
+    """Results cached to disk for reuse."""
+    return heavy_compute(params)
+
+@timeout(seconds=30)
+def risky_call():
+    """Raises TimeoutError if exceeds 30s."""
+    return external_api()
+
+
+
+
+

Type Conversion

+

Auto-convert between data frameworks:

+
    +
  • @torch_fn – Inputs to PyTorch tensors, outputs back to original type

  • +
  • @numpy_fn – Inputs to NumPy arrays

  • +
  • @pandas_fn – Inputs to pandas objects

  • +
  • @xarray_fn – Inputs to xarray objects

  • +
+
+
+

Batch Processing

+
    +
  • @batch_fn – Split input into batches with tqdm progress

  • +
  • @batch_numpy_fn – NumPy conversion + batching

  • +
  • @batch_torch_fn – PyTorch conversion + batching

  • +
  • @batch_pandas_fn – Pandas conversion + batching

  • +
+
+
+

Caching

+
    +
  • @cache_mem – In-memory function result caching

  • +
  • @cache_disk – Persistent disk-based caching

  • +
  • @cache_disk_async – Async disk caching

  • +
+
+
+

Utilities

+
    +
  • @deprecated(reason, forward_to) – Mark functions as deprecated

  • +
  • @not_implemented – Mark as not yet implemented

  • +
  • @timeout(seconds) – Enforce execution time limits

  • +
  • @preserve_doc – Preserve docstrings when wrapping

  • +
+
+
+

Auto-Ordering

+
from scitex.decorators import enable_auto_order
+
+enable_auto_order()
+# Now decorators are applied in optimal order regardless of code order
+
+
+
+
+

API Reference

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/dev.html b/src/scitex/_sphinx_html/source/api/dev.html new file mode 100644 index 000000000..ae4244ce6 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/dev.html @@ -0,0 +1,1800 @@ + + + + + + + + + Dev Module (stx.dev) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Dev Module (stx.dev)

+

Development tools and ecosystem management for the SciTeX package family.

+
+

Note

+

stx.dev delegates to the standalone +scitex-dev package. +Install with: pip install scitex-dev.

+
+
+

Overview

+

The dev module provides utilities for maintaining the SciTeX ecosystem of +packages, building documentation, managing versions, and creating +MCP/CLI wrappers for scientific tools. It is primarily used by package +maintainers and contributors rather than end users.

+
+
+

Ecosystem Management

+

The SciTeX ecosystem comprises 14+ packages. stx.dev provides a +unified registry and coordination tools.

+
import scitex as stx
+
+# List all ecosystem packages
+stx.dev.list_versions()
+
+# Check version consistency across the ecosystem
+stx.dev.check_versions()
+
+# Synchronize local clones
+stx.dev.ecosystem_sync()
+
+# Commit across multiple packages
+stx.dev.ecosystem_commit("fix: update shared constants")
+
+
+ + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Ecosystem Packages

Package

Purpose

scitex

Hub package (re-exports all modules)

scitex-io

File I/O for 30+ formats

scitex-stats

Statistical testing with auto-reporting

scitex-linter

AST-based convention checker

scitex-clew

Claim-evidence-workflow pipeline

figrecipe

Publication-quality plotting

scitex-dev

Development and ecosystem tools

scitex-notification

Multi-backend notifications

scitex-app

Runtime SDK for SciTeX applications

+
+
+

LLM-friendly Types

+

stx.dev provides structured return types designed for consumption +by both humans and AI agents:

+
from scitex.dev import Result, ErrorCode
+
+# Wrap function results for consistent handling
+result = Result(ok=True, data={"accuracy": 0.95})
+result = Result(ok=False, error=ErrorCode.NOT_FOUND, message="File missing")
+
+# Decorator to add return_as= parameter
+@stx.dev.supports_return_as
+def analyze(data):
+    return {"mean": data.mean()}
+
+analyze(data, return_as="json")    # JSON string
+analyze(data, return_as="dict")    # Python dict
+
+
+
+
+

MCP and CLI Wrappers

+

Convert Python functions into MCP tools or CLI commands:

+
from scitex.dev import wrap_as_mcp, wrap_as_cli
+
+def my_tool(path: str, threshold: float = 0.5) -> dict:
+    """Analyze a data file."""
+    ...
+
+# Register as MCP tool (for AI agent access)
+mcp_tool = wrap_as_mcp(my_tool)
+
+# Register as CLI command (for terminal access)
+cli_cmd = wrap_as_cli(my_tool)
+
+
+
+
+

Documentation

+

Build and search unified documentation across the ecosystem:

+
# Build Sphinx docs for a package
+stx.dev.build_docs("scitex-io")
+
+# Get docstring for any public function
+stx.dev.get_docs("stx.io.save")
+
+# Full-text search across all packages
+results = stx.dev.search("load_configs")
+
+
+
+
+

Hot Reload

+

Reload modules during interactive development:

+
stx.dev.reload("scitex.io")    # Re-import scitex.io from disk
+
+
+
+
+

HPC Testing

+

Submit test suites to HPC clusters and poll results:

+
scitex dev test-hpc --package scitex-io
+scitex dev test-hpc-poll --job-id 12345
+
+
+
+
+

Bulk Rename

+

Rename symbols across an entire codebase safely:

+
stx.dev.bulk_rename(
+    root="./src",
+    old="old_function_name",
+    new="new_function_name",
+    dry_run=True,    # Preview changes first
+)
+
+
+
+
+

API Reference

+

scitex-dev: Shared developer utilities for the SciTeX ecosystem.

+

Zero-dependency package providing: +- Docs aggregation and serving across all scitex packages +- Unified search across APIs, CLI, MCP tools, and documentation +- Version management and ecosystem registry +- Bulk rename with cross-reference updates +- LLM-friendly types (Result, ErrorCode, @supports_return_as)

+

Public API (20 functions):

+
# Docs
+get_docs, build_docs, search_docs
+
+# Search
+search
+
+# Versions
+list_versions, check_versions, get_mismatches, fix_mismatches
+
+# LLM-friendly types
+Result, ErrorCode, supports_return_as, SideEffect,
+classify_exception, handle_result, run_as_cli, run_as_mcp,
+result_to_mcp, wrap_as_mcp
+
+
+
+
+scitex.dev.get_docs(package=None, packages=None, format=None, page=None, *, _discover_fn=None, _root_fn=None, _sphinx_fn=None)[source]
+

Get documentation for one, several, or all installed SciTeX packages.

+
+
Resolution chain per package:
    +
  1. Pre-built _sphinx_html/ in installed package → fastest (production)

  2. +
  3. Sphinx _build/ available → use existing build

  4. +
  5. Neither → introspect from docstrings + signatures (always works)

  6. +
+
+
+
+
Parameters:
+
    +
  • package (Optional[str]) – Single package name (returns unwrapped result).

  • +
  • packages (Optional[list[str]]) – List of package names (returns dict keyed by name).

  • +
  • format (Optional[str]) – Output format — None for manifest, “json” for structured, +“html” for path to HTML directory.

  • +
  • page (Optional[str]) – Specific page name (only with format=”html” or “json”).

  • +
+
+
Returns:
+

the doc result directly (unwrapped). +- If packages=: dict mapping package name → doc result. +- If neither: dict of all discovered packages → doc result.

+
+
Return type:
+

Any

+
+
Raises:
+
    +
  • ValueError – If both package= and packages= are given.

  • +
  • LookupError – If a requested package is not found.

  • +
+
+
+
+ +
+
+scitex.dev.build_docs(package=None, output_dir=None, formats=None, *, _discover_fn=None, _sphinx_fn=None)[source]
+

Build docs from Sphinx source for one or all packages.

+
+
Parameters:
+
    +
  • package (Optional[str]) – Single package name. None = build all discovered.

  • +
  • output_dir (Optional[Path]) – Override output directory. Default: in-place (_build/).

  • +
  • formats (Optional[list[str]]) – List of builders (“html”, “json”). Default: [“html”].

  • +
+
+
Return type:
+

dict[str, Any]

+
+
Returns:
+

Dict mapping package name → build result (path or error).

+
+
Raises:
+
+
+
+
+ +
+
+scitex.dev.search_docs(query, package=None, packages=None, max_results=10, *, _discover_fn=None, _get_one_fn=None)[source]
+

Search documentation across one, several, or all SciTeX packages.

+

Simple keyword search over page titles and introspected content. +Stdlib only — no external search dependencies.

+
+
Parameters:
+
    +
  • query (str) – Search query string (case-insensitive keyword matching).

  • +
  • package (Optional[str]) – Search within a single package.

  • +
  • packages (Optional[list[str]]) – Search within specific packages.

  • +
  • max_results (int) – Maximum number of results to return.

  • +
+
+
Returns:
+

package, name, title, score, match_type. +Sorted by relevance score (descending).

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+scitex.dev.search(query, scope='all', package=None, packages=None, max_results=10, fuzzy=True)[source]
+

Search across the SciTeX ecosystem.

+
+
Query syntax:

“save figure” → match any term +‘“exact phrase”’ → match exact phrase +“+required term” → term must appear +“-excluded” → results with this term are removed

+
+
+
+
Parameters:
+
    +
  • query (str) – Search query (supports +required, -excluded, “phrases”).

  • +
  • scope (Literal['all', 'api', 'cli', 'mcp', 'docs']) – What to search — “all”, “api”, “cli”, “mcp”, or “docs”.

  • +
  • package (Optional[str]) – Limit to a single package.

  • +
  • packages (Optional[list[str]]) – Limit to specific packages.

  • +
  • max_results (int) – Maximum results to return.

  • +
  • fuzzy (bool) – Enable fuzzy matching via difflib (default True).

  • +
+
+
Returns:
+

package, name, title, score, scope, match_type. +Sorted by relevance (score descending).

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+scitex.dev.list_versions(packages=None)[source]
+

List versions for all ecosystem packages.

+
+
Parameters:
+

packages (list[str] | None) – List of package names to check. If None, checks all ecosystem packages.

+
+
Returns:
+

Version information for each package.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+scitex.dev.check_versions(packages=None)[source]
+

Check version consistency and return detailed status.

+
+
Parameters:
+

packages (list[str] | None) – List of package names to check. If None, checks all ecosystem packages.

+
+
Returns:
+

Detailed version check results with overall summary.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+scitex.dev.get_mismatches(packages=None)[source]
+

Return packages with non-ok status and their issues.

+
+
Parameters:
+

packages (list[str] | None) – Package names to check. None = all ecosystem packages.

+
+
Returns:
+

{package_name: {status, issues, local, git, remote}} for non-ok packages.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+scitex.dev.fix_mismatches(hosts=None, packages=None, local=True, remote=True, confirm=False, config=None)[source]
+

Detect version mismatches and fix them.

+

Combines detect + fix_local + fix_remote. Kept for backward compatibility. +Prefer using the individual functions for clarity.

+

Safety: defaults to preview only. Pass confirm=True to execute.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+scitex.dev.get_ecosystem_versions(packages=None)[source]
+

Return a flat {pkg_name: installed_version} dict for the ecosystem.

+

Thin wrapper over list_versions for consumers that just need the +installed-version string (Django health endpoints, Docker HEALTHCHECK +scripts, dashboards). Skips all the git / PyPI / pyproject cross- +check detail.

+
+
Parameters:
+

packages (list[str] | None) – Package names to check. None = all ecosystem packages.

+
+
Returns:
+

{"scitex": "2.27.3", "figrecipe": "0.28.1", "scitex-io": None}. +None means the package is not installed.

+
+
Return type:
+

dict[str, str | None]

+
+
+
+

Examples

+
>>> from scitex_dev import get_ecosystem_versions
+>>> vers = get_ecosystem_versions()
+>>> vers["scitex"]
+'2.27.3'
+
+
+
+
+ +
+
+class scitex.dev.Result(success, data=None, error=None, error_code=None, context=<factory>, side_effects=<factory>, hints_on_success=<factory>, hints_on_warning=<factory>, hints_on_error=<factory>, idempotent=False, version=None)[source]
+

Bases: Generic[T]

+

Structured result wrapping function output with metadata.

+
+
+success
+

Whether the operation succeeded.

+
+
Type:
+

bool

+
+
+
+ +
+
+data
+

The return value on success; None on failure.

+
+
Type:
+

T | None

+
+
+
+ +
+
+error
+

Human-readable error message on failure.

+
+
Type:
+

str | None

+
+
+
+ +
+
+error_code
+

Machine-readable error code (e.g. “E001”).

+
+
Type:
+

str | None

+
+
+
+ +
+
+context
+

Additional context (file paths, parameters, etc.).

+
+
Type:
+

dict

+
+
+
+ +
+
+side_effects
+

Description of mutations performed.

+
+
Type:
+

list[str]

+
+
+
+ +
+
+hints_on_success
+

Related tools the consumer might want (light, “see also” style). +Reserved for future use — currently empty.

+
+
Type:
+

list[str]

+
+
+
+ +
+
+hints_on_warning
+

Hints for partial success, deprecation, or approaching limits. +Reserved for future use — currently empty.

+
+
Type:
+

list[str]

+
+
+
+ +
+
+hints_on_error
+

Recovery guidance for expected failures (FAQ-style). +Accepts None, “”, a single string, or a list.

+
+
Type:
+

list[str]

+
+
+
+ +
+
+idempotent
+

Whether the operation is safe to retry.

+
+
Type:
+

bool

+
+
+
+ +
+
+version
+

API version for response schema evolution.

+
+
Type:
+

str | None

+
+
+
+ +
+
+__post_init__()[source]
+

Normalize hint fields (accept None, “”, single string, or list).

+
+ +
+
+to_dict()[source]
+

Convert to a plain dictionary, stripping None values.

+
+
Return type:
+

dict

+
+
+
+ +
+
+to_json(indent=2)[source]
+

Serialize to a JSON string.

+
+
Return type:
+

str

+
+
+
+ +
+
+property exit_code: int
+

Map error_code to a CLI exit code.

+
+ +
+
+classmethod json_schema()[source]
+

Return JSON Schema describing the Result envelope.

+
+
Return type:
+

dict

+
+
+
+ +
+ +
+
+class scitex.dev.ErrorCode(*values)[source]
+

Bases: str, Enum

+

Machine-readable error codes for structured error responses.

+
+
+property exit_code: int
+

POSIX-style exit code for this error category.

+
+ +
+ +
+
+exception scitex.dev.ScitexError(message, *, code=ErrorCode.INTERNAL, remediation=None)[source]
+

Bases: Exception

+

Canonical SciTeX exception for structured failures.

+

Use when the failure crosses an MCP / CLI / HTTP boundary and the caller +(often an LLM agent) needs a machine-readable category plus a human +remediation hint. For plain Python errors raised inside a script, +prefer the standard library exceptions (ValueError, FileNotFoundError, +etc.) — see general/03_interface/01_python-api/09_error-handling.md.

+
+
Parameters:
+
    +
  • message (str) – Human-readable description of the failure.

  • +
  • code (ErrorCode) – Machine-readable category. Defaults to ErrorCode.INTERNAL.

  • +
  • remediation (str | None) – How to fix it (e.g. "pip install scitex-io[h5]").

  • +
+
+
+
+

Examples

+
>>> raise ScitexError(
+...     "h5py not installed",
+...     code=ErrorCode.DEPENDENCY,
+...     remediation="pip install scitex-io[h5]",
+... )
+
+
+
+
+
+to_dict()[source]
+

Serialize to the structured shape consumed by MCP tools and –json.

+
+
Return type:
+

dict[str, str]

+
+
+
+ +
+ +
+
+scitex.dev.classify_exception(exc)[source]
+

Classify an exception into an ErrorCode.

+

Checks for a .error_code attribute first (SciTeXError convention), +then falls back to built-in exception type mapping.

+
+
Return type:
+

ErrorCode

+
+
+
+ +
+
+scitex.dev.try_import_optional(module_path, attr=None, *, extra=None, pkg=None, package=None)[source]
+

Import module_path; return None on ImportError.

+
+
Parameters:
+
    +
  • module_path (str) – Module to import. Leading . triggers relative resolution against +package (mirrors importlib.import_module()).

  • +
  • attr (str | None) – If given, return getattr(module, attr) instead of the module. +A missing attribute is treated identically to a failed import.

  • +
  • extra (str | None) – Name of the pip extra that pulls this dependency (used for the +install hint surfaced via last_install_hint()).

  • +
  • pkg (str | None) – Distribution name owning the extra (e.g. "scitex-io").

  • +
  • package (str | None) – Anchor for relative imports.

  • +
+
+
Returns:
+

The imported module/attribute, or None on failure.

+
+
Return type:
+

Any

+
+
+
+ +
+
+scitex.dev.last_install_hint(name)[source]
+

Return the most recently recorded install hint for name (or None).

+
+
Return type:
+

InstallHint | None

+
+
+
+ +
+
+class scitex.dev.InstallHint(module, extra, pkg)[source]
+

Bases: object

+

Metadata to help a caller surface a useful install message.

+
+ +
+
+scitex.dev.supports_return_as(fn)[source]
+

Add return_as="result" support to a function.

+
    +
  • return_as=None (default): data returned, exceptions raised.

  • +
  • return_as="result": wrapped in Result.

  • +
  • Any other value: passed through to the original function.

  • +
+
+

Examples

+
>>> @supports_return_as
+... def add(a: int, b: int) -> int:
+...     return a + b
+>>> add(1, 2)
+3
+>>> result = add(1, 2, return_as="result")
+>>> result.success
+True
+>>> result.data
+3
+
+
+
+
+
Return type:
+

Callable

+
+
+
+ +
+
+class scitex.dev.SideEffect(type, target, undoable=False)[source]
+

Bases: object

+

A declared side effect of a function.

+
+
+type
+

Category (file_create, file_modify, file_delete, network, +state_change, cache_write).

+
+
Type:
+

str

+
+
+
+ +
+
+target
+

What is affected (file path, URL, description).

+
+
Type:
+

str

+
+
+
+ +
+
+undoable
+

Whether the effect can be reversed.

+
+
Type:
+

bool

+
+
+
+ +
+ +
+
+scitex.dev.handle_result(result, as_json=False, file=None)[source]
+

Format and print a Result, return the exit code.

+
+
Parameters:
+
    +
  • result (Result) – The structured result to display.

  • +
  • as_json (bool) – If True, output full JSON. If False, human-friendly text.

  • +
  • file (Any) – Output stream. Defaults to stdout/stderr based on success.

  • +
+
+
Returns:
+

Exit code suitable for sys.exit().

+
+
Return type:
+

int

+
+
+
+ +
+
+scitex.dev.run_as_cli(fn, as_json=False, **kwargs)[source]
+

Call a @supports_return_as function and exit with proper code.

+
+
Parameters:
+
    +
  • fn (Callable) – A function decorated with @supports_return_as.

  • +
  • as_json (bool) – If True, output full JSON.

  • +
  • **kwargs (Any) – Arguments to pass to fn.

  • +
+
+
Return type:
+

None

+
+
+
+ +
+
+scitex.dev.wrap_as_cli(fn, as_json=False, **kwargs)[source]
+

Call any function and display its result via CLI.

+

Like wrap_as_mcp but for terminal output. Wraps any plain +function in Result, formats based on as_json, and exits +with proper exit code.

+
+
Parameters:
+
    +
  • fn (Callable) – Any callable returning data or raising exceptions.

  • +
  • as_json (bool) – If True, output full JSON Result. If False, human-friendly text.

  • +
  • **kwargs (Any) – Arguments to pass to fn.

  • +
+
+
Return type:
+

None

+
+
+
+ +
+
+scitex.dev.run_as_mcp(fn, **kwargs)[source]
+

Call a @supports_return_as function and return MCP-ready JSON.

+
+
Parameters:
+
    +
  • fn (Callable) – A function decorated with @supports_return_as.

  • +
  • **kwargs (Any) – Arguments to pass to fn.

  • +
+
+
Returns:
+

JSON string with the full Result structure.

+
+
Return type:
+

str

+
+
+
+ +
+
+scitex.dev.wrap_as_mcp(fn, *, side_effects=None, hints_on_error=None, idempotent=False, **kwargs)[source]
+

Call any function and wrap its return in Result JSON.

+

Unlike run_as_mcp (which requires @supports_return_as), +this wraps any plain function. Use this to retrofit existing +handlers without modifying the underlying function.

+
+
Parameters:
+
    +
  • fn (Callable) – Any callable returning data or raising exceptions.

  • +
  • side_effects (list[str] | None) – Declared side effects (e.g. ["file_create: /tmp/out.csv"]).

  • +
  • hints_on_error (list[str] | None) – Recovery guidance for expected failures (FAQ-style).

  • +
  • idempotent (bool) – Whether the operation is safe to retry.

  • +
  • **kwargs (Any) – Arguments to pass to fn.

  • +
+
+
Returns:
+

JSON string with Result structure.

+
+
Return type:
+

str

+
+
+
+ +
+
+async scitex.dev.async_wrap_as_mcp(coro_fn, *, side_effects=None, hints_on_error=None, idempotent=False, **kwargs)[source]
+

Async version of wrap_as_mcp for async handlers.

+
+
Parameters:
+
    +
  • coro_fn (Callable) – Any async callable (coroutine function) returning data +or raising exceptions.

  • +
  • side_effects (list[str] | None) – Declared side effects.

  • +
  • hints_on_error (list[str] | None) – Recovery guidance for expected failures (FAQ-style).

  • +
  • idempotent (bool) – Whether the operation is safe to retry.

  • +
  • **kwargs (Any) – Arguments to pass to coro_fn.

  • +
+
+
Returns:
+

JSON string with Result structure.

+
+
Return type:
+

str

+
+
+
+ +
+
+scitex.dev.result_to_mcp(result)[source]
+

Convert an existing Result to MCP-ready JSON.

+
+
Return type:
+

str

+
+
+
+ +
+
+scitex.dev.json_option(fn)[source]
+

Click decorator: adds --json flag as as_json parameter.

+

Uses lazy import click so scitex-dev stays stdlib-only.

+
+
Return type:
+

Callable

+
+
+
+ +
+
+scitex.dev.dry_run_option(fn)[source]
+

Click decorator: adds --dry-run flag.

+

Uses lazy import click so scitex-dev stays stdlib-only.

+
+
Return type:
+

Callable

+
+
+
+ +
+
+scitex.dev.add_json_argument(parser)[source]
+

Add --json flag to an argparse parser.

+
+
Return type:
+

None

+
+
+
+ +
+
+scitex.dev.add_dry_run_argument(parser)[source]
+

Add --dry-run flag to an argparse parser.

+
+
Return type:
+

None

+
+
+
+ +
+
+scitex.dev.sync_all(hosts=None, packages=None, stash=True, install=True, safe=True, confirm=False, config=None, *, sync_host_fn=None, enabled_hosts_fn=None)[source]
+

Sync packages across all enabled hosts.

+

Safety: defaults to preview only. Pass confirm=True to execute. +Parallel: hosts are synced concurrently by default.

+
+
Parameters:
+
    +
  • hosts (list[str] | None) – Host names to sync. None = all enabled hosts.

  • +
  • packages (list[str] | None) – Package names. None = host-specific defaults.

  • +
  • stash (bool) – Git stash before pull.

  • +
  • install (bool) – Pip install after pull.

  • +
  • safe (bool) – If True (default), skip packages whose remote working copy is +ahead of / diverged from upstream (never clobber unpushed work).

  • +
  • confirm (bool) – If False (default), preview only (dry run). +If True, execute the sync operation.

  • +
  • config (DevConfig | None) – Configuration.

  • +
+
+
Returns:
+

{host_name: {package: result}}.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+scitex.dev.sync_host(host, packages=None, stash=True, install=True, safe=True, confirm=False, config=None, *, host_packages_fn=None)[source]
+

Sync packages to a remote host via SSH.

+

Safety: defaults to preview only. Pass confirm=True to execute.

+

Steps per package: ahead-check (if safe), git stash, git pull, +pip install -e ., git stash pop.

+
+
Parameters:
+
    +
  • host (HostConfig) – Target host configuration.

  • +
  • packages (list[str] | None) – Package names to sync. None = use host’s configured packages.

  • +
  • stash (bool) – Git stash before pull (default True).

  • +
  • install (bool) – Pip install after pull (default True).

  • +
  • safe (bool) – If True (default), pre-check each remote working copy and skip +packages whose HEAD is ahead of / diverged from upstream so we +never clobber unpushed commits.

  • +
  • confirm (bool) – If False (default), preview only (dry run). +If True, execute the sync operation.

  • +
  • config (DevConfig | None) – Configuration. Loaded from default if None.

  • +
+
+
Returns:
+

Per-package results: {package: {status, commands|output, error}}. +status includes ok, skipped_ahead, skipped_diverged, +missing, error, timeout, or dry_run.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+scitex.dev.sync_local(packages=None, confirm=False, config=None, jobs=1, on_progress=None)[source]
+

Install all local editable packages.

+

Safety: defaults to preview only. Pass confirm=True to execute.

+
+
Parameters:
+
    +
  • packages (list[str] | None) – Package names. None = all configured packages.

  • +
  • confirm (bool) – If False (default), preview only. +If True, execute pip install -e.

  • +
  • config (DevConfig | None) – Configuration.

  • +
  • jobs (int) – Parallel installs. 1 = serial (default). 0 or negative = all CPUs.

  • +
  • on_progress (callable | None) – Optional callback f(idx, total, name, status, elapsed) invoked +as each package finishes.

  • +
+
+
Returns:
+

{package: {status, output|commands}}.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+scitex.dev.sync_tags(packages=None, confirm=False, config=None)[source]
+

Push local tags for all packages to origin.

+

Safety: defaults to preview only. Pass confirm=True to execute.

+
+
Parameters:
+
    +
  • packages (list[str] | None) – Package names. None = all configured packages.

  • +
  • confirm (bool) – If False (default), preview only. +If True, execute git push –tags.

  • +
  • config (DevConfig | None) – Configuration.

  • +
+
+
Returns:
+

{package: {status, tag, output|commands}}.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+scitex.dev.remote_diff(host=None, packages=None, config=None)[source]
+

Show git diff on remote host(s). Read-only operation.

+
+
Parameters:
+
    +
  • host (str | None) – Host name. None = first enabled host.

  • +
  • packages (list[str] | None) – Package names. None = host-configured defaults.

  • +
  • config (DevConfig | None) – Configuration.

  • +
+
+
Returns:
+

{host_name: {package: {status, files, diff_stat, diff}}}.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+scitex.dev.remote_commit(host, packages=None, message=None, push=True, confirm=False, config=None)[source]
+

Commit dirty changes on a remote host and optionally push to origin.

+

Safety: defaults to preview only. Pass confirm=True to execute.

+
+
Parameters:
+
    +
  • host (str) – Host name (required).

  • +
  • packages (list[str] | None) – Package names. None = host-configured defaults.

  • +
  • message (str | None) – Commit message. Auto-generated if not provided.

  • +
  • push (bool) – Push to origin after commit (default True).

  • +
  • confirm (bool) – If False (default), preview only (dry run).

  • +
  • config (DevConfig | None) – Configuration.

  • +
+
+
Returns:
+

{package: {status, commands|output}}.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+scitex.dev.pull_local(packages=None, confirm=False, stash=True, config=None)[source]
+

Pull latest from origin to local repos.

+

Safety: defaults to preview only. Pass confirm=True to execute.

+
+
Parameters:
+
    +
  • packages (list[str] | None) – Package names. None = all configured packages.

  • +
  • confirm (bool) – If False (default), preview only.

  • +
  • stash (bool) – If True (default), stash local changes before pull and pop after. +If False and repo is dirty, pull proceeds as-is (may fail).

  • +
  • config (DevConfig | None) – Configuration.

  • +
+
+
Returns:
+

{package: {status, output|commands, stashed}}.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/diagram.html b/src/scitex/_sphinx_html/source/api/diagram.html new file mode 100644 index 000000000..2de08e10e --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/diagram.html @@ -0,0 +1,768 @@ + + + + + + + + + Diagram Module (stx.diagram) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Diagram Module (stx.diagram)

+

Paper-optimized diagram generation with Mermaid and Graphviz backends.

+
+

Quick Reference

+
from scitex.diagram import Diagram
+
+# Create from Python
+d = Diagram(type="workflow", title="Analysis Pipeline")
+d.add_node("load", "Load Data", shape="stadium")
+d.add_node("proc", "Process", shape="rounded", emphasis="primary")
+d.add_node("save", "Save Results", shape="stadium", emphasis="success")
+d.add_edge("load", "proc")
+d.add_edge("proc", "save")
+
+# Export
+d.to_mermaid("pipeline.mmd")
+d.to_graphviz("pipeline.dot")
+
+# Or from YAML specification
+d = Diagram.from_yaml("pipeline.diagram.yaml")
+
+
+
+
+

YAML Specification

+
type: workflow
+title: SciTeX Analysis Pipeline
+
+paper:
+  column: single          # single or double
+  mode: publication       # draft or publication
+  reading_direction: left_to_right
+
+layout:
+  groups:
+    Input: [load, preprocess]
+    Analysis: [analyze, test]
+
+nodes:
+  - id: load
+    label: Load Data
+    shape: stadium
+  - id: analyze
+    label: Statistical Test
+    shape: rounded
+    emphasis: primary
+
+edges:
+  - from: load
+    to: analyze
+
+
+
+
+

Node Shapes

+

box, rounded, stadium, diamond, circle, codeblock

+
+
+

Node Emphasis

+
    +
  • normal – Default (dark)

  • +
  • primary – Key nodes (blue)

  • +
  • success – Positive outcomes (green)

  • +
  • warning – Issues (red)

  • +
  • muted – Secondary (gray)

  • +
+
+
+

Diagram Types

+
    +
  • workflow – Left-to-right sequential processes

  • +
  • decision – Top-to-bottom decision trees

  • +
  • pipeline – Data pipeline stages

  • +
  • hierarchy – Tree structures

  • +
  • comparison – Side-by-side comparison

  • +
+
+
+

Paper Modes

+
    +
  • draft – Full arrows, medium spacing, all edges visible

  • +
  • publication – Tight spacing, return edges invisible, optimized for column width

  • +
+
+
+

Backends

+
    +
  • Mermaid (.mmd) – Web/markdown rendering

  • +
  • Graphviz DOT (.dot) – Tighter layouts, render with dot -Tpng

  • +
  • YAML (.diagram.yaml) – Semantic spec, human/LLM-readable

  • +
+
+
+

API Reference

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/dict.html b/src/scitex/_sphinx_html/source/api/dict.html new file mode 100644 index 000000000..153e63732 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/dict.html @@ -0,0 +1,674 @@ + + + + + + + + + dict Module (stx.dict) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

dict Module (stx.dict)

+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/dsp.html b/src/scitex/_sphinx_html/source/api/dsp.html new file mode 100644 index 000000000..a910a4fae --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/dsp.html @@ -0,0 +1,742 @@ + + + + + + + + + DSP Module (stx.dsp) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

DSP Module (stx.dsp)

+

Digital signal processing tools for filtering, spectral analysis, +and time-frequency decomposition.

+
+

Quick Reference

+
import scitex as stx
+import numpy as np
+
+# Generate test signal
+fs = 1000  # Hz
+t = np.arange(0, 1, 1/fs)
+signal = np.sin(2 * np.pi * 10 * t) + 0.5 * np.sin(2 * np.pi * 50 * t)
+
+# Filtering
+filtered = stx.dsp.filt.bandpass(signal, fs=fs, low=5, high=30)
+low_pass = stx.dsp.filt.lowpass(signal, fs=fs, cutoff=20)
+high_pass = stx.dsp.filt.highpass(signal, fs=fs, cutoff=5)
+
+# Power spectral density
+freqs, psd = stx.dsp.psd(signal, fs=fs)
+
+# Band powers
+powers = stx.dsp.band_powers(signal, fs=fs, bands={
+    "delta": (1, 4),
+    "theta": (4, 8),
+    "alpha": (8, 13),
+    "beta": (13, 30),
+})
+
+# Hilbert transform (analytic signal)
+analytic = stx.dsp.hilbert(signal)
+amplitude = np.abs(analytic)
+phase = np.angle(analytic)
+
+# Wavelet decomposition
+coeffs = stx.dsp.wavelet(signal, fs=fs, freqs=np.arange(1, 50))
+
+
+
+
+

Available Functions

+

Filtering (stx.dsp.filt)

+
    +
  • bandpass(signal, fs, low, high)

  • +
  • lowpass(signal, fs, cutoff)

  • +
  • highpass(signal, fs, cutoff)

  • +
  • notch(signal, fs, freq)

  • +
+

Spectral Analysis

+
    +
  • psd(signal, fs) – Power spectral density

  • +
  • band_powers(signal, fs, bands) – Power in frequency bands

  • +
  • wavelet(signal, fs, freqs) – Continuous wavelet transform

  • +
+

Time-Frequency

+
    +
  • hilbert(signal) – Analytic signal via Hilbert transform

  • +
  • pac(signal, fs) – Phase-amplitude coupling

  • +
+

Utilities

+
    +
  • demo_sig(fs, duration) – Generate demo signals for testing

  • +
  • crop(signal, start, end, fs) – Crop signal to time range

  • +
  • detect_ripples(signal, fs) – Detect sharp-wave ripples

  • +
+
+
+

API Reference

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/etc.html b/src/scitex/_sphinx_html/source/api/etc.html new file mode 100644 index 000000000..6723ab46d --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/etc.html @@ -0,0 +1,777 @@ + + + + + + + + + etc Module (stx.etc) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

etc Module (stx.etc)

+

Utility functions for miscellaneous tasks.

+

This module provides utility functions that don’t fit into other categories, +such as keyboard input handling for interactive programs.

+
+
+scitex.etc.count(*, printer=<built-in function print>, sleeper=<built-in function sleep>)[source]
+

Print an incrementing counter forever, sleeping 1s between values.

+
+
Parameters:
+
    +
  • printer (callable, optional) – Output sink, defaults to the builtin print. Injectable test seam.

  • +
  • sleeper (callable, optional) – One-arg sleep callable, defaults to time.sleep. Injectable so +tests can run without real delay and bound the loop.

  • +
+
+
+
+ +
+
+scitex.etc.count_grids(params_grid)[source]
+

Return the total number of combinations in a parameter grid.

+
+
Parameters:
+

params_grid (dict) – Mapping of parameter name -> list of candidate values.

+
+
Returns:
+

Product of the lengths of all value lists.

+
+
Return type:
+

int

+
+
+
+ +
+
+scitex.etc.yield_grids(params_grid, random=False)[source]
+

Yield every parameter combination from a grid as a dict.

+
+
Parameters:
+
    +
  • params_grid (dict) – Mapping of parameter name -> list of candidate values.

  • +
  • random (bool) – If True, yield the combinations in shuffled order (default False).

  • +
+
+
Yields:
+

dict – One combination, mapping each parameter name to a single value.

+
+
+
+

Example

+
>>> grid = {"a": [1, 2], "b": ["x", "y"]}
+>>> list(yield_grids(grid))
+[{'a': 1, 'b': 'x'}, {'a': 1, 'b': 'y'}, {'a': 2, 'b': 'x'}, {'a': 2, 'b': 'y'}]
+
+
+
+
+ +
+
+scitex.etc.search(patterns, strings, only_perfect_match=False, as_bool=False, ensure_one=False)[source]
+

Search for patterns in strings using regular expressions.

+
+
Parameters:
+
    +
  • patterns (str or list of str) – The pattern(s) to search for. A single string or a list of strings.

  • +
  • strings (str or list of str) – The string(s) to search in. A single string or a list of strings.

  • +
  • only_perfect_match (bool) – If True, only exact matches are considered (default False).

  • +
  • as_bool (bool) – If True, return a boolean mask instead of indices (default False).

  • +
  • ensure_one (bool) – If True, assert exactly one match is found (default False).

  • +
+
+
Returns:
+

    +
  • If as_bool is False: (indices, matched_strings) where +indices is a list of int positions of matches.

  • +
  • If as_bool is True: (mask, matched_strings) where mask is +a boolean sequence (numpy array when numpy is installed, else a list) +flagging matched positions.

  • +
+

+
+
Return type:
+

tuple

+
+
+
+

Example

+
>>> patterns = ['orange', 'banana']
+>>> strings = ['apple', 'orange', 'apple', 'apple_juice', 'banana', 'orange_juice']
+>>> search(patterns, strings)
+([1, 4, 5], ['orange', 'banana', 'orange_juice'])
+
+
+
>>> search('orange', strings)
+([1, 5], ['orange', 'orange_juice'])
+
+
+
+
+ +
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/events.html b/src/scitex/_sphinx_html/source/api/events.html new file mode 100644 index 000000000..60253984b --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/events.html @@ -0,0 +1,674 @@ + + + + + + + + + events Module (stx.events) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

events Module (stx.events)

+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/gen.html b/src/scitex/_sphinx_html/source/api/gen.html new file mode 100644 index 000000000..08f0d46ca --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/gen.html @@ -0,0 +1,704 @@ + + + + + + + + + Gen Module (stx.gen) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Gen Module (stx.gen)

+

General utilities for path management, shell commands, and system operations.

+
+

Note

+

Session management has moved to @stx.session (see Core Concepts). +The scitex.gen.start() / scitex.gen.close() pattern is deprecated.

+
+
+

Quick Reference

+
import scitex as stx
+
+# Timestamped output directories
+path = stx.gen.mk_spath("./results")
+# → ./results/20260213_143022/
+
+# Run shell commands
+stx.gen.run("ls -la")
+
+# Clipboard operations
+stx.gen.copy("text to clipboard")
+text = stx.gen.paste()
+
+# String to valid path
+stx.gen.title2path("My Experiment #1")
+# → "my_experiment_1"
+
+
+
+
+

API Reference

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/gists.html b/src/scitex/_sphinx_html/source/api/gists.html new file mode 100644 index 000000000..28abe7150 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/gists.html @@ -0,0 +1,701 @@ + + + + + + + + + gists Module (stx.gists) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

gists Module (stx.gists)

+

SigmaPlot macro conversion utilities.

+
+
+scitex.gists.SigMacro_processFigure_S()[source]
+

Deprecated: Use sigmacro_process_figure_s() instead.

+
+ +
+
+scitex.gists.SigMacro_toBlue()[source]
+

Deprecated: Use sigmacro_to_blue() instead.

+
+ +
+
+scitex.gists.sigmacro_process_figure_s()[source]
+

Print a macro for SigmaPlot (v12.0) to format a panel.

+

Please refer to the ‘Automating Routine Tasks’ section of the official documentation.

+
+ +
+
+scitex.gists.sigmacro_to_blue()[source]
+

Print a macro for SigmaPlot (v12.0) that changes the color and style of the selected object.

+

Please refer to the ‘Automating Routine Tasks’ section of the official documentation.

+
+ +
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/git.html b/src/scitex/_sphinx_html/source/api/git.html new file mode 100644 index 000000000..c6f246355 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/git.html @@ -0,0 +1,674 @@ + + + + + + + + + git Module (stx.git) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

git Module (stx.git)

+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/index.html b/src/scitex/_sphinx_html/source/api/index.html new file mode 100644 index 000000000..82b206122 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/index.html @@ -0,0 +1,1181 @@ + + + + + + + + + API Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

API Reference

+

SciTeX is organized into focused modules. +All modules are accessible via import scitex as stx followed by stx.<module>.

+ + + + + + + +
+

Utilities

+ +
+ +
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/introspect.html b/src/scitex/_sphinx_html/source/api/introspect.html new file mode 100644 index 000000000..1f37a9814 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/introspect.html @@ -0,0 +1,739 @@ + + + + + + + + + Introspect Module (stx.introspect) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Introspect Module (stx.introspect)

+

IPython-like code inspection for exploring Python packages.

+
+

Quick Reference

+
import scitex as stx
+
+# Function signature (like IPython's func?)
+stx.introspect.q("scitex.stats.test_ttest_ind")
+# → name, signature, parameters, return type
+
+# Full source code (like IPython's func??)
+stx.introspect.qq("scitex.stats.test_ttest_ind")
+# → complete source with line numbers
+
+# List module members (like enhanced dir())
+stx.introspect.dir("scitex.plt", filter="public", kind="functions")
+
+# Recursive API tree
+df = stx.introspect.list_api("scitex", max_depth=2)
+
+
+
+
+

IPython-Style Shortcuts

+
    +
  • q(dotted_path) – Signature and parameters (like func?)

  • +
  • qq(dotted_path) – Full source code (like func??)

  • +
  • dir(dotted_path, filter, kind) – List members with filtering

  • +
+

Filters: "public", "private", "dunder", "all"

+

Kinds: "functions", "classes", "data", "modules"

+
+
+

Documentation

+
    +
  • get_docstring(path, format) – Extract docstrings ("raw", "parsed", "summary")

  • +
  • get_exports(path) – Get __all__ exports

  • +
  • find_examples(path) – Find usage examples in tests/examples

  • +
+
+
+

Type Analysis

+
    +
  • get_type_hints_detailed(path) – Full type annotation analysis

  • +
  • get_class_hierarchy(path) – Inheritance tree (MRO + subclasses)

  • +
  • get_class_annotations(path) – Class variable annotations

  • +
+
+
+

Code Analysis

+
    +
  • get_imports(path, categorize) – All imports (AST-based, grouped by stdlib/third-party/local)

  • +
  • get_dependencies(path, recursive) – Module dependency tree

  • +
  • get_call_graph(path, max_depth) – Function call graph (with timeout protection)

  • +
+
+
+

API Tree

+
# Generate full module tree as DataFrame
+df = stx.introspect.list_api("scitex", max_depth=3, docstring=True)
+
+
+
+
+

API Reference

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/io.html b/src/scitex/_sphinx_html/source/api/io.html new file mode 100644 index 000000000..026ee7adf --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/io.html @@ -0,0 +1,847 @@ + + + + + + + + + I/O Module (stx.io) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

I/O Module (stx.io)

+

Unified file I/O for 30+ scientific data formats. A single save() +and load() interface handles format detection by file extension, +so you never need to remember which library reads .npy vs .h5 +vs .parquet.

+
+

Note

+

Core format handlers are provided by the standalone +scitex-io package. +stx.io adds integration with stx.session (provenance tracking), +stx.clew (claim verification), and automatic CSV export for figures.

+
+
+

Supported Formats

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Category

Extensions

Tabular

.csv, .tsv, .xlsx, .xls, .parquet, .feather

Array

.npy, .npz, .mat, .zarr, .h5 / .hdf5

Config

.yaml, .yml, .json, .toml, .ini, .conf

Figure

.png, .jpg, .jpeg, .svg, .pdf, .tiff

Text

.txt, .log, .md, .rst

Audio

.wav, .mp3, .flac

Serialized

.pkl, .pickle, .joblib

Bibliography

.bib

+
+
+

Quick Start

+
import scitex as stx
+import pandas as pd
+import numpy as np
+
+# --- Save and load a DataFrame ---
+df = pd.DataFrame({"x": [1, 2, 3], "y": [4, 5, 6]})
+stx.io.save(df, "results.csv")
+df_loaded = stx.io.load("results.csv")
+
+# --- Save and load a NumPy array ---
+arr = np.random.randn(100, 3)
+stx.io.save(arr, "data.npy")
+arr_loaded = stx.io.load("data.npy")
+
+# --- Save a figure (PNG + CSV data export) ---
+fig, ax = stx.plt.subplots()
+ax.stx_line([1, 2, 3, 4, 5], id="my_data")
+stx.io.save(fig, "plot.png")
+# Creates: plot.png, plot.csv, plot.yaml
+
+
+
+
+

Key Functions

+
+

save(obj, path, **kwargs)

+

Save any supported object to a file. The format is determined by the +file extension. Built-in features beyond simple save:

+
    +
  • Auto directory creation – no os.makedirs() needed

  • +
  • Path resolution – relative paths resolve to <script_name>_out/<path>

  • +
  • Symlinkssymlink_from_cwd=True for short access paths

  • +
  • Save logging – prints file path and size on success

  • +
  • Clew hash tracking – file hashes recorded automatically for verification

  • +
  • Figure CSV export – saves plot data alongside image files

  • +
+
# Format detected from extension
+stx.io.save(fig, "figure.pdf")       # Also exports CSV + YAML recipe
+stx.io.save(df, "output.parquet")    # DataFrame
+stx.io.save({"lr": 0.001}, "config.yaml")  # Dict
+stx.io.save(arr, "weights.npy")      # NumPy array
+
+# Symlink for convenient access
+stx.io.save(df, "results/data.csv", symlink_from_cwd=True)
+
+
+
+
+

load(path)

+

Load data from a file. Returns the appropriate Python object +(DataFrame, ndarray, dict, etc.).

+
df = stx.io.load("results.csv")          # -> pd.DataFrame
+arr = stx.io.load("weights.npy")         # -> np.ndarray
+cfg = stx.io.load("config.yaml")         # -> dict
+data = stx.io.load("experiment.h5")      # -> dict of arrays
+
+
+
+
+

load_configs(pattern="./config/*.yaml")

+

Load and merge multiple YAML configuration files into a single +DotDict. Used internally by @stx.session to build the +CONFIG object.

+
CONF = stx.io.load_configs("./config/*.yaml")
+print(CONF.MODEL.hidden_size)
+
+
+
+
+

list_formats()

+

List all registered format handlers.

+
fmts = stx.io.list_formats()
+# ['.csv', '.h5', '.hdf5', '.json', '.mat', '.npy', ...]
+
+
+
+
+
+

HDF5 and Zarr Exploration

+

For hierarchical formats, use the explorer interface:

+
# HDF5
+stx.io.explore_h5("data.h5")            # Print tree structure
+stx.io.has_h5_key("data.h5", "/group/dataset")
+
+# Zarr
+stx.io.explore_zarr("data.zarr")
+stx.io.has_zarr_key("data.zarr", "/group/array")
+
+
+
+
+

Format Registry

+

Register custom loaders and savers for new file extensions:

+
@stx.io.register_loader(".custom")
+def load_custom(path, **kwargs):
+    with open(path) as f:
+        return parse_custom(f.read())
+
+@stx.io.register_saver(".custom")
+def save_custom(obj, path, **kwargs):
+    with open(path, "w") as f:
+        f.write(serialize_custom(obj))
+
+
+
+
+

Caching

+

Built-in load caching for repeated reads of the same file:

+
stx.io.configure_cache(maxsize=128)
+data = stx.io.load("big_file.h5")    # First call: reads from disk
+data = stx.io.load("big_file.h5")    # Second call: from cache
+
+stx.io.get_cache_info()               # Cache hit/miss statistics
+stx.io.clear_load_cache()             # Flush the cache
+
+
+
+
+

API Reference

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/linalg.html b/src/scitex/_sphinx_html/source/api/linalg.html new file mode 100644 index 000000000..e40a9fc4e --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/linalg.html @@ -0,0 +1,674 @@ + + + + + + + + + linalg Module (stx.linalg) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

linalg Module (stx.linalg)

+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/linter.html b/src/scitex/_sphinx_html/source/api/linter.html new file mode 100644 index 000000000..41714b545 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/linter.html @@ -0,0 +1,853 @@ + + + + + + + + + Linter Module (stx.linter) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Linter Module (stx.linter)

+

AST-based Python linter enforcing SciTeX conventions for reproducible +scientific code. Uses a plugin architecture where each downstream +package contributes its own rules.

+
+

Note

+

stx.linter wraps the standalone +scitex-linter package. +Install with: pip install scitex-linter.

+
+
+

Quick Start

+
# Check a file
+scitex linter check my_script.py
+
+# Check with specific severity
+scitex linter check my_script.py --severity warning
+
+# List all rules
+scitex linter list-rules
+
+# Filter by category
+scitex linter list-rules --category session
+
+
+
from scitex_linter import check_file, list_rules
+
+# Check a file
+results = check_file("my_script.py")
+
+# List available rules
+rules = list_rules()
+
+
+
+
+

Rule Categories

+

47+ rules across 7 categories, contributed by multiple packages:

+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Prefix

Category

Source

Examples

STX-S

Session

scitex-linter

Missing @stx.session, missing if __name__ guard, missing return, +load_configs() result not UPPER_CASE, magic numbers in module scope

STX-I

Import

scitex-linter

Using import mngs instead of import scitex, bare import numpy, +wildcard imports

STX-IO

I/O

scitex-io

Using open() instead of stx.io, hardcoded paths, +using pd.read_csv directly

STX-P

Plotting

figrecipe

Using plt.show() instead of stx.io.save, missing set_xyt, +using raw matplotlib calls

STX-ST

Statistics

scitex-stats

Using scipy.stats directly instead of stx.stats, +missing multiple comparison correction

STX-PA

Path

scitex-linter

Hardcoded absolute paths, missing stx.gen.mk_spath

STX-FM

Format

figrecipe

Non-snake_case names, magic numbers, missing docstrings

+
+
+

Severity Levels

+
    +
  • error – Must fix (breaks reproducibility or correctness)

  • +
  • warning – Should fix (best practice violations)

  • +
  • info – Suggestions for improvement

  • +
+
+
+

Plugin Architecture

+

Each downstream package defines its own linter rules via the +scitex_linter.plugins entry point. The linter discovers and +aggregates rules automatically at runtime.

+
# In a downstream package's pyproject.toml
+[project.entry-points."scitex_linter.plugins"]
+my_package = "my_package._linter_plugin:get_plugin"
+
+
+

The plugin function returns a dictionary of rules, call patterns, +axes hints, and AST checker classes:

+
def get_plugin():
+    from ._rules import MyRule001, MyRule002
+    return {
+        "rules": [MyRule001, MyRule002],
+        "call_rules": {"my_func": "Use stx.my_func() instead"},
+        "axes_hints": {},
+        "checkers": [],
+    }
+
+
+
+
+

Interfaces

+

The linter is available through four interfaces:

+

CLI (via scitex):

+
scitex linter check script.py
+scitex linter list-rules --category io --severity warning
+
+
+

Standalone CLI:

+
scitex-linter check script.py
+
+
+

Python API:

+
from scitex_linter import check_file, check_source, list_rules
+
+results = check_file("script.py")
+results = check_source("import numpy as np\n...")
+rules = list_rules(category="io", severity="warning")
+
+
+

Flake8 Plugin:

+
flake8 --select=STX script.py
+
+
+

MCP Tools (for AI agents):

+
linter_check(path="script.py", severity="warning")
+linter_list_rules(category="session")
+linter_check_source(source="...", filepath="<stdin>")
+
+
+
+
+

Configuration

+

Configure via pyproject.toml:

+
[tool.scitex-linter]
+severity = "warning"
+ignore = ["STX-S002", "STX-FM001"]
+
+
+
+
+

Example Output

+
my_script.py:3:1: STX-I001 [warning] Use 'import scitex as stx' instead of 'import mngs'
+my_script.py:15:5: STX-IO003 [error] Use 'stx.io.load()' instead of 'pd.read_csv()'
+my_script.py:22:1: STX-S001 [warning] Missing '@stx.session' decorator on main()
+
+3 issues found (1 error, 2 warnings)
+
+
+
+
+

API Reference

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/logging.html b/src/scitex/_sphinx_html/source/api/logging.html new file mode 100644 index 000000000..4e7c4458f --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/logging.html @@ -0,0 +1,1341 @@ + + + + + + + + + Logging Module (stx.logging) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Logging Module (stx.logging)

+

Unified logging with file/console output, custom warnings, exception +hierarchy, and stream redirection.

+
+

Quick Reference

+
import scitex as stx
+from scitex import logging
+
+# Get a logger
+logger = logging.getLogger(__name__)
+logger.info("Processing data")
+logger.success("Analysis complete")   # Custom level (31)
+logger.fail("Model diverged")         # Custom level (35)
+
+# Configure globally
+logging.configure(level="DEBUG", log_file="./run.log")
+logging.set_level("WARNING")
+
+# Temporary file logging
+with logging.log_to_file("analysis.log"):
+    logger.info("This goes to both console and file")
+
+# Warnings
+logging.warn("Large dataset", category=logging.PerformanceWarning)
+logging.warn_deprecated("old_func", "new_func", version="3.0")
+logging.filterwarnings("ignore", category=logging.UnitWarning)
+
+
+
+
+

Log Levels

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Level

Value

Description

DEBUG

10

Detailed diagnostic information

INFO

20

General operational messages

WARNING

30

Something unexpected but not fatal

SUCCESS

31

Custom: operation completed successfully

FAIL

35

Custom: operation failed (non-fatal)

ERROR

40

Serious problem

CRITICAL

50

Program may not continue

+
+
+

Warning Categories

+
    +
  • SciTeXWarning – Base warning class

  • +
  • UnitWarning – SI unit convention issues

  • +
  • StyleWarning – Formatting issues

  • +
  • SciTeXDeprecationWarning – Deprecated features

  • +
  • PerformanceWarning – Performance issues

  • +
  • DataLossWarning – Potential data loss

  • +
+
+
+

Exception Hierarchy

+

All inherit from SciTeXError:

+
    +
  • I/O: IOError, FileFormatError, SaveError, LoadError

  • +
  • Config: ConfigurationError, ConfigFileNotFoundError, ConfigKeyError

  • +
  • Path: PathError, InvalidPathError, PathNotFoundError

  • +
  • Data: DataError, ShapeError, DTypeError

  • +
  • Plotting: PlottingError, FigureNotFoundError, AxisError

  • +
  • Stats: StatsError, TestError

  • +
  • Scholar: ScholarError, SearchError, PDFDownloadError, DOIResolutionError

  • +
  • NN: NNError, ModelError

  • +
  • Template: TemplateError, TemplateViolationError

  • +
+
+
+

Stream Redirection

+
from scitex.logging import tee
+import sys
+
+# Redirect stdout/stderr to log files
+sys.stdout, sys.stderr = tee(sys, sdir="./output")
+
+
+

The @stx.session decorator handles this automatically.

+
+
+

API Reference

+
+
+scitex.logging.getLogger(name=None)[source]
+

Return a logger with the specified name, creating it if necessary.

+

If no name is specified, return the root logger.

+
+ +
+
+scitex.logging.configure(level='info', log_file=None, enable_file=True, enable_console=True, capture_prints=True, max_file_size=10485760, backup_count=5)[source]
+

Configure logging for SciTeX with both console and file output.

+
+
Parameters:
+
    +
  • level (Union[str, int]) – Log level (string or logging constant)

  • +
  • log_file (Optional[str]) – Path to log file (default: ~/.scitex/logs/scitex-YYYY-MM-DD.log)

  • +
  • enable_file (bool) – Whether to enable file logging

  • +
  • enable_console (bool) – Whether to enable console logging

  • +
  • capture_prints (bool) – Whether to capture print() statements to logs

  • +
  • max_file_size (int) – Maximum size of log file before rotation (default: 10MB)

  • +
  • backup_count (int) – Number of backup files to keep (default: 5)

  • +
+
+
+
+ +
+
+scitex.logging.set_level(level)[source]
+

Set global log level for all SciTeX loggers.

+
+ +
+
+scitex.logging.get_level()[source]
+

Get current global log level.

+
+ +
+
+scitex.logging.enable_file_logging(enabled=True)[source]
+

Enable or disable file logging globally.

+
+ +
+
+scitex.logging.is_file_logging_enabled()[source]
+

Check if file logging is enabled.

+
+ +
+
+scitex.logging.get_log_path()[source]
+

Get the current log file path.

+
+ +
+
+class scitex.logging.Tee(stream, log_path, verbose=True)[source]
+
+
+write(data)[source]
+
+
Return type:
+

None

+
+
+
+ +
+
+flush()[source]
+
+
Return type:
+

None

+
+
+
+ +
+
+isatty()[source]
+
+
Return type:
+

bool

+
+
+
+ +
+
+fileno()[source]
+
+
Return type:
+

int

+
+
+
+ +
+
+property buffer
+
+ +
+
+close()[source]
+

Explicitly close the log file.

+
+ +
+ +
+
+scitex.logging.tee(sys, sdir=None, verbose=True)[source]
+

Redirects stdout and stderr to both console and log files.

+
+

Example

+
>>> import sys
+>>> sys.stdout, sys.stderr = tee(sys)
+>>> print("abc")  # stdout
+>>> print(1 / 0)  # stderr
+
+
+
+
+
Parameters:
+
    +
  • sys_module (module) – System module containing stdout and stderr

  • +
  • sdir (str, optional) – Directory for log files

  • +
  • verbose (bool, default=True) – Whether to print log file locations

  • +
+
+
Returns:
+

Wrapped stdout and stderr objects

+
+
Return type:
+

tuple[Any, Any]

+
+
+
+ +
+
+scitex.logging.log_to_file(file_path, level=10, mode='w', formatter=None)[source]
+

Context manager to temporarily log all output to a specific file.

+
+
Usage:

import scitex_logging as logging +logger = logging.getLogger(__name__)

+
+
with logging.log_to_file(“/path/to/log.txt”):

logger.info(“This goes to both console and /path/to/log.txt”) +logger.success(“This too!”)

+
+
+
+
+
+
Parameters:
+
    +
  • file_path (Union[str, Path]) – Path to log file

  • +
  • level (int) – Logging level for this handler (default: DEBUG)

  • +
  • mode (str) – File mode (‘w’ for overwrite, ‘a’ for append)

  • +
  • formatter (Optional[Formatter]) – Custom formatter (default: SciTeXFileFormatter)

  • +
+
+
Yields:
+

The file handler (can be ignored)

+
+
+
+ +
+
+exception scitex.logging.SciTeXWarning[source]
+

Base warning class for all SciTeX warnings.

+
+ +
+
+exception scitex.logging.UnitWarning[source]
+

Warning for axis label unit issues (educational for SI conventions).

+

Raised when: +- Axis labels are missing units +- Units use parentheses instead of brackets (SI prefers []) +- Units use division instead of negative exponents (m/s vs m·s⁻¹)

+
+ +
+
+exception scitex.logging.StyleWarning[source]
+

Warning for style/formatting issues.

+
+ +
+
+exception scitex.logging.SciTeXDeprecationWarning[source]
+

Warning for deprecated SciTeX features.

+
+ +
+
+exception scitex.logging.PerformanceWarning[source]
+

Warning for performance issues.

+
+ +
+
+exception scitex.logging.DataLossWarning[source]
+

Warning for potential data loss.

+
+ +
+
+scitex.logging.warn(message, category=<class 'scitex_logging._warnings.SciTeXWarning'>, stacklevel=2)[source]
+

Emit a warning (like warnings.warn but integrated with scitex.logging).

+
+
Parameters:
+
    +
  • message (str) – Warning message

  • +
  • category (Type[SciTeXWarning]) – Warning category (default: SciTeXWarning)

  • +
  • stacklevel (int) – Stack level for source location (default: 2 = caller)

  • +
+
+
Return type:
+

None

+
+
+
+

Examples

+
>>> import scitex.logging as logging
+>>> from scitex.logging import UnitWarning
+>>> logging.warn("X axis has no units", UnitWarning)
+
+
+
+
+ +
+
+scitex.logging.filterwarnings(action, category=<class 'scitex_logging._warnings.SciTeXWarning'>, message=None)[source]
+

Control warning behavior (like warnings.filterwarnings).

+
+
Parameters:
+
    +
  • action (str) – One of: +- “ignore”: Never show this warning +- “error”: Raise as exception +- “always”: Always show +- “default”: Show first occurrence per location +- “once”: Show only once total +- “module”: Show once per module

  • +
  • category (Type[SciTeXWarning]) – Warning category (default: SciTeXWarning = all)

  • +
  • message (Optional[str]) – Regex pattern to match warning message (not implemented yet)

  • +
+
+
Return type:
+

None

+
+
+
+

Examples

+
>>> import scitex.logging as logging
+>>> from scitex.logging import UnitWarning
+>>> logging.filterwarnings("ignore", category=UnitWarning)
+
+
+
+
+ +
+
+scitex.logging.resetwarnings()[source]
+

Reset all warning filters to default behavior.

+
+
Return type:
+

None

+
+
+
+ +
+
+scitex.logging.warn_deprecated(old_name, new_name, version=None)[source]
+

Issue a deprecation warning.

+
+
Return type:
+

None

+
+
+
+ +
+
+scitex.logging.warn_performance(operation, suggestion)[source]
+

Issue a performance warning.

+
+
Return type:
+

None

+
+
+
+ +
+
+scitex.logging.warn_data_loss(operation, detail)[source]
+

Issue a data loss warning.

+
+
Return type:
+

None

+
+
+
+ +
+
+exception scitex.logging.SciTeXError(message, context=None, suggestion=None)[source]
+

Base Exception class for all SciTeX errors.

+
+
+__init__(message, context=None, suggestion=None)[source]
+

Initialize SciTeX error with detailed information.

+
+
Parameters:
+
    +
  • message (str) – The error message

  • +
  • context (Optional[dict]) – Additional context information (e.g., file paths, variable values)

  • +
  • suggestion (Optional[str]) – Suggested fix or action

  • +
+
+
+
+ +
+ +
+
+exception scitex.logging.ConfigurationError(message, context=None, suggestion=None)[source]
+

Raised when there are issues with SciTeX configuration.

+
+ +
+
+exception scitex.logging.ConfigFileNotFoundError(filepath)[source]
+

Raised when a required configuration file is not found.

+
+ +
+
+exception scitex.logging.ConfigKeyError(key, available_keys=None)[source]
+

Raised when a required configuration key is missing.

+
+ +
+
+exception scitex.logging.IOError(message, context=None, suggestion=None)[source]
+

Base class for input/output related errors.

+
+ +
+
+exception scitex.logging.FileFormatError(filepath, expected_format=None, actual_format=None)[source]
+

Raised when file format is not supported or incorrect.

+
+ +
+
+exception scitex.logging.SaveError(filepath, reason)[source]
+

Raised when saving data fails.

+
+ +
+
+exception scitex.logging.LoadError(filepath, reason)[source]
+

Raised when loading data fails.

+
+ +
+
+exception scitex.logging.ScholarError(message, context=None, suggestion=None)[source]
+

Base class for scholar module errors.

+
+ +
+
+exception scitex.logging.SearchError(query, source, reason)[source]
+

Raised when paper search fails.

+
+ +
+
+exception scitex.logging.EnrichmentError(paper_title, reason)[source]
+

Raised when paper enrichment fails.

+
+ +
+
+exception scitex.logging.PDFDownloadError(url, reason)[source]
+

Raised when PDF download fails.

+
+ +
+
+exception scitex.logging.DOIResolutionError(doi, reason)[source]
+

Raised when DOI resolution fails.

+
+ +
+
+exception scitex.logging.PDFExtractionError(filepath, reason)[source]
+

Raised when PDF text extraction fails.

+
+ +
+
+exception scitex.logging.BibTeXEnrichmentError(bibtex_file, reason)[source]
+

Raised when BibTeX enrichment fails.

+
+ +
+
+exception scitex.logging.TranslatorError(translator_name, reason)[source]
+

Raised when Zotero translator operations fail.

+
+ +
+
+exception scitex.logging.AuthenticationError(provider, reason='')[source]
+

Raised when authentication fails.

+
+ +
+
+exception scitex.logging.PlottingError(message, context=None, suggestion=None)[source]
+

Base class for plotting-related errors.

+
+ +
+
+exception scitex.logging.FigureNotFoundError(fig_id)[source]
+

Raised when attempting to operate on a non-existent figure.

+
+ +
+
+exception scitex.logging.AxisError(message, axis_info=None)[source]
+

Raised when there are issues with plot axes.

+
+ +
+
+exception scitex.logging.DataError(message, context=None, suggestion=None)[source]
+

Base class for data processing errors.

+
+ +
+
+exception scitex.logging.ShapeError(expected_shape, actual_shape, operation)[source]
+

Raised when data shapes are incompatible.

+
+ +
+
+exception scitex.logging.DTypeError(expected_dtype, actual_dtype, operation)[source]
+

Raised when data types are incompatible.

+
+ +
+
+exception scitex.logging.PathError(message, context=None, suggestion=None)[source]
+

Base class for path-related errors.

+
+ +
+
+exception scitex.logging.InvalidPathError(path, reason)[source]
+

Raised when a path is invalid or doesn’t follow SciTeX conventions.

+
+ +
+
+exception scitex.logging.PathNotFoundError(path)[source]
+

Raised when a required path doesn’t exist.

+
+ +
+
+exception scitex.logging.TemplateError(message, context=None, suggestion=None)[source]
+

Base class for template-related errors.

+
+ +
+
+exception scitex.logging.TemplateViolationError(filepath, violation)[source]
+

Raised when SciTeX template is not followed.

+
+ +
+
+exception scitex.logging.NNError(message, context=None, suggestion=None)[source]
+

Base class for neural network module errors.

+
+ +
+
+exception scitex.logging.ModelError(model_name, reason)[source]
+

Raised when there are issues with neural network models.

+
+ +
+
+exception scitex.logging.StatsError(message, context=None, suggestion=None)[source]
+

Base class for statistics module errors.

+
+ +
+
+exception scitex.logging.TestError(test_name, reason)[source]
+

Raised when statistical tests fail.

+
+ +
+
+scitex.logging.check_path(path)[source]
+

Validate a path according to SciTeX conventions.

+
+
Return type:
+

None

+
+
+
+ +
+
+scitex.logging.check_file_exists(filepath)[source]
+

Check if a file exists.

+
+
Return type:
+

None

+
+
+
+ +
+
+scitex.logging.check_shape_compatibility(shape1, shape2, operation)[source]
+

Check if two shapes are compatible for an operation.

+
+
Return type:
+

None

+
+
+
+ +
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/media.html b/src/scitex/_sphinx_html/source/api/media.html new file mode 100644 index 000000000..ea32ec3d4 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/media.html @@ -0,0 +1,686 @@ + + + + + + + + + media Module (stx.media) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

media Module (stx.media)

+

scitex_etc.media — Media handling (detection, rendering, display).

+
+
Submodules:
+
render — Detect, classify, and format media for various targets

(chat pane, terminal overlay, markdown embed).

+
+
+
+
Usage:

from scitex_etc.media import render

+

refs = render.detect(text, root_path=”/home/user/proj”) +render.show(“figure.png”, target=”terminal”)

+
+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/module.html b/src/scitex/_sphinx_html/source/api/module.html new file mode 100644 index 000000000..a335a1579 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/module.html @@ -0,0 +1,674 @@ + + + + + + + + + module Module (stx.module) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

module Module (stx.module)

+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/msword.html b/src/scitex/_sphinx_html/source/api/msword.html new file mode 100644 index 000000000..1a2d5fa92 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/msword.html @@ -0,0 +1,1798 @@ + + + + + + + + + msword Module (stx.msword) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

msword Module (stx.msword)

+

MS Word (DOCX) import/export utilities for SciTeX.

+

This module provides high-level functions to convert between +MS Word .docx files and SciTeX’s internal writer document model.

+
+

Strategy:

+
    +
  • Word users write text only (paragraphs, minimal formatting)

  • +
  • SciTeX handles: figures, tables, references, LaTeX generation

  • +
  • SciTeX JSON is the “source of truth”, Word is just a view/edit layer

  • +
+
+
+

Typical usage:

+
+

from scitex_msword import load_docx, save_docx, list_profiles

+

# Import from Word +doc = load_docx(“input.docx”, profile=”generic”)

+

# Manipulate via scitex.writer… +# doc.normalize()

+

# Export to Word (different journal template) +save_docx(doc, “output.docx”, profile=”mdpi-ijerph”)

+
+
+
+

Available profiles:

+
    +
  • generic: Standard Word with Heading 1/2/3

  • +
  • mdpi-ijerph: MDPI IJERPH journal template

  • +
  • resna-2025: RESNA 2025 scientific paper template

  • +
  • iop-double-anonymous: IOP double-anonymous template

  • +
+
+
+
+scitex.msword.load_docx(path, profile=None, extract_images=True)[source]
+

Load a DOCX file and convert it into a SciTeX writer document.

+
+
Parameters:
+
    +
  • path (str | Path) – Path to the .docx file.

  • +
  • profile (str | None) – Optional profile name that specifies how to interpret Word styles +(e.g., “mdpi-ijerph”, “resna-2025”). If None, “generic” is used.

  • +
  • extract_images (bool) – If True, extract embedded images and store references.

  • +
+
+
Returns:
+

A SciTeX writer document structure containing: +- blocks: List of document blocks (headings, paragraphs, captions, etc.) +- metadata: Profile and source file information +- images: Extracted image references (if extract_images=True) +- references: Parsed reference entries

+
+
Return type:
+

dict[str, Any]

+
+
+
+

Examples

+
>>> from scitex.msword import load_docx
+>>> doc = load_docx("manuscript.docx", profile="mdpi-ijerph")
+>>> print(doc["metadata"]["profile"])
+'mdpi-ijerph'
+
+
+
+
+ +
+
+scitex.msword.save_docx(writer_doc, path, profile=None, overwrite=True, template_path=None)[source]
+

Save a SciTeX writer document as a DOCX file.

+
+
Parameters:
+
    +
  • writer_doc (dict[str, Any] | Any) – SciTeX writer document instance to export.

  • +
  • path (str | Path) – Output path for the .docx file.

  • +
  • profile (str | None) – Optional profile name that controls how sections, headings, +figures, tables and references are mapped to Word styles. +If None, “generic” is used.

  • +
  • overwrite (bool) – If False and the file already exists, raises FileExistsError.

  • +
  • template_path (str | Path | None) – Optional path to a Word template (.dotx/.docx) to use as base. +This allows using journal-specific formatting.

  • +
+
+
Returns:
+

The path to the written .docx file.

+
+
Return type:
+

Path

+
+
+
+

Examples

+
>>> from scitex.msword import save_docx
+>>> save_docx(doc, "submission_resna_2025.docx", profile="resna-2025")
+PosixPath('submission_resna_2025.docx')
+
+
+
+
+ +
+
+scitex.msword.convert_docx_to_tex(input_path, output_path, profile=None, *, image_dir=None, link_images=True, link_mode='by-number', normalize_headings=True, validate=True)[source]
+

Convert a DOCX file directly to LaTeX.

+

This is a convenience function that: +1. Loads the DOCX file into SciTeX intermediate format +2. (Optionally) normalizes headings +3. (Optionally) links figure captions to images +4. (Optionally) validates the document and adds warnings +5. Exports to LaTeX (including figures via image_dir)

+
+
Parameters:
+
    +
  • input_path (str | Path) – Path to the input .docx file.

  • +
  • output_path (str | Path) – Path for the output .tex file.

  • +
  • profile (str | None) – Word profile for interpreting styles +(e.g., “resna-2025”, “iop-double-anonymous”).

  • +
  • image_dir (str | Path | None) – Directory where extracted figure image files will be saved. +If None, the LaTeX exporter will create “<tex_stem>_figures” +next to output_path.

  • +
  • link_images (bool) – Whether to link figure captions to extracted images so that +LaTeX can generate includegraphics inside figure environments.

  • +
  • link_mode (str) –

    Strategy for linking captions to images: +- “by-number”: Figure 1 -> first image, Figure 2 -> second image… +- “by-proximity”: assign images in document order, useful when

    +
    +

    figure numbers and image order don’t match.

    +
    +

  • +
  • normalize_headings (bool) – If True, apply common heading normalizations +(e.g., “intro” -> “Introduction”).

  • +
  • validate (bool) – If True, run basic structural checks and populate +doc[“warnings”] with any issues.

  • +
+
+
Returns:
+

The path to the written .tex file.

+
+
Return type:
+

Path

+
+
+
+

Examples

+
>>> from scitex.msword import convert_docx_to_tex
+>>> convert_docx_to_tex(
+...     "RESNA 2025 Scientific Paper Template.docx",
+...     "manuscript.tex",
+...     profile="resna-2025",
+...     image_dir="figures",
+... )
+PosixPath('manuscript.tex')
+
+
+
+
+ +
+
+scitex.msword.list_profiles()[source]
+

List available MS Word profiles.

+
+
Returns:
+

List of profile names (e.g., [“generic”, “mdpi-ijerph”, …]).

+
+
Return type:
+

list[str]

+
+
+
+

Examples

+
>>> from scitex.msword import list_profiles
+>>> profiles = list_profiles()
+>>> "generic" in profiles
+True
+
+
+
+
+ +
+
+scitex.msword.get_profile(name)[source]
+

Get a Word profile by name.

+
+
Parameters:
+

name (str | None) – Profile name. If None, “generic” is used.

+
+
Returns:
+

The requested profile.

+
+
Return type:
+

BaseWordProfile

+
+
Raises:
+

KeyError – If the profile name is unknown.

+
+
+
+

Examples

+
>>> from scitex.msword import get_profile
+>>> profile = get_profile("mdpi-ijerph")
+>>> profile.columns
+1
+
+
+
+
+ +
+
+scitex.msword.register_profile(profile)[source]
+

Register a custom Word profile.

+
+
Parameters:
+

profile (BaseWordProfile) – The profile to register.

+
+
Return type:
+

None

+
+
+
+

Examples

+
>>> from scitex.msword import BaseWordProfile, register_profile
+>>> custom = BaseWordProfile(
+...     name="my-journal",
+...     description="My custom journal template",
+...     heading_styles={1: "Title", 2: "Subtitle"},
+... )
+>>> register_profile(custom)
+>>> "my-journal" in list_profiles()
+True
+
+
+
+
+ +
+
+class scitex.msword.BaseWordProfile(name, description, heading_styles=<factory>, caption_style='Caption', normal_style='Normal', reference_section_titles=<factory>, figure_caption_prefixes=<factory>, table_caption_prefixes=<factory>, list_styles=<factory>, equation_style=None, columns=1, double_anonymous=False, body_font=None, body_font_size_pt=None, heading_background_hex=None, line_spacing=None, post_import_hooks=<factory>, pre_export_hooks=<factory>)[source]
+

Bases: object

+

Base configuration for mapping between DOCX and SciTeX writer documents.

+
+
+name
+

Profile identifier (e.g., “mdpi-ijerph”).

+
+
Type:
+

str

+
+
+
+ +
+
+description
+

Human-readable description.

+
+
Type:
+

str

+
+
+
+ +
+
+heading_styles
+

Mapping from section depth (1, 2, 3…) to Word style names +(e.g., {1: “Heading 1”, 2: “Heading 2”}).

+
+
Type:
+

dict[int, str]

+
+
+
+ +
+
+caption_style
+

Word style name used for figure/table captions.

+
+
Type:
+

str

+
+
+
+ +
+
+normal_style
+

Default paragraph style.

+
+
Type:
+

str

+
+
+
+ +
+
+reference_section_titles
+

Titles that indicate the start of the reference section.

+
+
Type:
+

list[str]

+
+
+
+ +
+
+figure_caption_prefixes
+

Prefixes that identify figure captions (e.g., [“Figure”, “Fig.”]).

+
+
Type:
+

list[str]

+
+
+
+ +
+
+table_caption_prefixes
+

Prefixes that identify table captions (e.g., [“Table”]).

+
+
Type:
+

list[str]

+
+
+
+ +
+
+list_styles
+

Mapping for list styles (bullet, numbered).

+
+
Type:
+

dict[str, str]

+
+
+
+ +
+
+equation_style
+

Style name for equations, if any.

+
+
Type:
+

str | None

+
+
+
+ +
+
+columns
+

Number of columns in the layout (1 or 2).

+
+
Type:
+

int

+
+
+
+ +
+
+double_anonymous
+

Whether this profile requires double-anonymous formatting.

+
+
Type:
+

bool

+
+
+
+ +
+
+name: str
+
+ +
+
+description: str
+
+ +
+
+heading_styles: Dict[int, str]
+
+ +
+
+caption_style: str = 'Caption'
+
+ +
+
+normal_style: str = 'Normal'
+
+ +
+
+reference_section_titles: List[str]
+
+ +
+
+figure_caption_prefixes: List[str]
+
+ +
+
+table_caption_prefixes: List[str]
+
+ +
+
+list_styles: Dict[str, str]
+
+ +
+
+equation_style: str | None = None
+
+ +
+
+columns: int = 1
+
+ +
+
+double_anonymous: bool = False
+
+ +
+
+body_font: str | None = None
+
+ +
+
+body_font_size_pt: float | None = None
+
+ +
+
+heading_background_hex: str | None = None
+
+ +
+
+line_spacing: float | None = None
+
+ +
+
+post_import_hooks: List[Callable]
+
+ +
+
+pre_export_hooks: List[Callable]
+
+ +
+ +
+
+class scitex.msword.WordReader(profile, extract_images=True)[source]
+

Bases: object

+

Read a DOCX file and convert it into a SciTeX writer document.

+

This reader focuses on: +- Sections (via heading styles) +- Plain paragraphs +- Figure/table captions (via caption style) +- Embedded images extraction +- References section boundary detection +- Basic formatting (bold, italic)

+

The output is a structured intermediate representation that can be +easily fed into scitex.writer or exported to LaTeX/other formats.

+
+
+__init__(profile, extract_images=True)[source]
+
+
Parameters:
+
    +
  • profile (BaseWordProfile) – Mapping between Word styles and SciTeX writer semantics.

  • +
  • extract_images (bool) – Whether to extract embedded images from the document.

  • +
+
+
+
+ +
+
+read(path)[source]
+

Read a DOCX file and return a SciTeX writer document.

+
+
Parameters:
+

path (Path) – Path to the DOCX file.

+
+
Returns:
+

SciTeX writer document structure with keys: +- blocks: List of document blocks +- metadata: Profile and source information +- images: Extracted image data (if extract_images=True) +- references: Parsed reference entries +- warnings: List of conversion warnings

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+ +
+
+class scitex.msword.WordWriter(profile, template_path=None)[source]
+

Bases: object

+

Export a SciTeX writer document to a DOCX file.

+

This writer handles: +- Section headings with proper styles +- Paragraphs with formatting +- Figure and table captions +- References section +- Image embedding +- Journal-specific template application

+
+
+__init__(profile, template_path=None)[source]
+
+
Parameters:
+
    +
  • profile (BaseWordProfile) – Mapping from writer structures to Word styles.

  • +
  • template_path (Optional[Path]) – Optional path to a Word template (.dotx/.docx) to use as base.

  • +
+
+
+
+ +
+
+write(writer_doc, path)[source]
+

Write a SciTeX writer document to a DOCX file.

+
+
Parameters:
+
    +
  • writer_doc (Union[Dict[str, Any], Any]) – Writer document or intermediate structure.

  • +
  • path (Path) – Output path for the DOCX file.

  • +
+
+
Return type:
+

None

+
+
+
+ +
+ +
+ +

Link figure captions to images by matching order.

+

This function pairs figure captions with images based on their +sequential order in the document. Each figure caption is assigned +an image_hash that corresponds to the image at the same position.

+
+
Parameters:
+

doc (Dict[str, Any]) – SciTeX writer document with ‘blocks’ and ‘images’ keys.

+
+
Returns:
+

The same document with image_hash added to figure captions.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+

Examples

+
>>> from scitex.msword import load_docx
+>>> from scitex.msword.utils import link_captions_to_images
+>>> doc = load_docx("manuscript.docx")
+>>> doc = link_captions_to_images(doc)
+>>> # Now captions have image_hash for LaTeX export
+
+
+
+
+ +
+ +

Link figure captions to images by document proximity.

+

This function uses the image blocks (type=”image”) that are inserted +at their actual positions in the document body. It finds the nearest +unlinked image block to each figure caption.

+
+
Parameters:
+

doc (Dict[str, Any]) – SciTeX writer document.

+
+
Returns:
+

Document with image_hash added to captions.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+scitex.msword.normalize_section_headings(doc)[source]
+

Normalize section headings for consistency.

+

Converts common section titles to standard academic format: +- “intro” -> “Introduction” +- “method” -> “Methods” +- etc.

+
+
Parameters:
+

doc (Dict[str, Any]) – SciTeX writer document.

+
+
Returns:
+

Document with normalized headings.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+scitex.msword.validate_document(doc)[source]
+

Validate document structure and add warnings.

+

Checks for common issues: +- Missing required sections +- Unmatched caption numbers +- Empty references section +- Duplicate figure numbers

+
+
Parameters:
+

doc (Dict[str, Any]) – SciTeX writer document.

+
+
Returns:
+

Document with warnings added.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+scitex.msword.create_post_import_hook(*functions)[source]
+

Create a composite post_import_hook from multiple functions.

+
+
Parameters:
+

*functions (callable) – Functions to apply in sequence.

+
+
Returns:
+

A single hook that applies all functions.

+
+
Return type:
+

callable

+
+
+
+

Examples

+
>>> from scitex.msword.utils import (
+...     link_captions_to_images,
+...     normalize_section_headings,
+...     create_post_import_hook,
+... )
+>>> hook = create_post_import_hook(
+...     link_captions_to_images,
+...     normalize_section_headings,
+... )
+>>> # Use with custom profile
+>>> profile.post_import_hooks = [hook]
+
+
+
+
+ +
+
+scitex.msword.diff_docx(a, b, *, include_run_diff=True)[source]
+

Compute paragraph-level diff between two DOCX documents.

+
+
Parameters:
+
    +
  • a (Union[str, Path, Document]) – Inputs to compare. May be paths or already-loaded Documents.

  • +
  • b (Union[str, Path, Document]) – Inputs to compare. May be paths or already-loaded Documents.

  • +
  • include_run_diff (bool) – If True, modify operations include a runs_changed field +listing the run-level formatting deltas.

  • +
+
+
Returns:
+

Each entry is one of:

+
{"op": "equal",  "index": int, "text_a": str, "text_b": str}
+{"op": "insert", "index": int, "text_a": None, "text_b": str}
+{"op": "delete", "index": int, "text_a": str,  "text_b": None}
+{"op": "modify", "index": int, "text_a": str,  "text_b": str,
+ "runs_changed": [...]}
+
+
+

index refers to the paragraph index in document b for +equal/insert/modify operations, and to the paragraph +index in document a for delete operations.

+

+
+
Return type:
+

List[Dict[str, Any]]

+
+
+
+

Examples

+
>>> from scitex_msword.diff import diff_docx
+>>> ops = diff_docx("v15.docx", "v16.docx")
+>>> changes = [o for o in ops if o["op"] != "equal"]
+
+
+
+
+ +
+
+scitex.msword.summarize_diff(ops)[source]
+

Convenience helper: count operations by type.

+
+
Parameters:
+

ops (List[Dict[str, Any]]) – Output of diff_docx().

+
+
Returns:
+

Mapping {"equal": n, "insert": n, "delete": n, "modify": n}.

+
+
Return type:
+

Dict[str, int]

+
+
+
+ +
+
+scitex.msword.mark_additions(document, runs, color='turquoise')[source]
+

Highlight the runs that the operator (or agent) added to the document.

+
+
Parameters:
+
    +
  • document (Document) – The Document to mutate in place.

  • +
  • runs (Iterable[Tuple[int, int]]) – Targets to highlight. Out-of-range indices are skipped silently.

  • +
  • color (str) – Color name. See module docstring for the supported palette.

  • +
+
+
Returns:
+

The same Document object, mutated.

+
+
Return type:
+

Document

+
+
+
+

Examples

+
>>> from scitex_msword.highlights import mark_additions
+>>> doc = mark_additions(doc, [(3, 0), (5, 2)])  # default turquoise
+
+
+
+
+ +
+
+scitex.msword.mark_modifications(document, runs, color='magenta')[source]
+

Highlight the runs that the operator (or agent) modified in the document.

+
+
Parameters:
+
    +
  • document (Document) – The Document to mutate in place.

  • +
  • runs (Iterable[Tuple[int, int]]) – Targets to highlight. Out-of-range indices are skipped silently.

  • +
  • color (str) – Color name. See module docstring for the supported palette.

  • +
+
+
Returns:
+

The same Document object, mutated.

+
+
Return type:
+

Document

+
+
+
+

Examples

+
>>> from scitex_msword.highlights import mark_modifications
+>>> doc = mark_modifications(doc, [(7, 1)])  # default magenta
+
+
+
+
+ +
+
+scitex.msword.extract_highlights(document, by_color=True)[source]
+

Extract highlighted runs from a document, grouped by color.

+
+
Parameters:
+
    +
  • document (Document) – The document to scan.

  • +
  • by_color (bool) – If True (default), return {color_name: [run_info, ...]}. +If False, return a single {"all": [run_info, ...]} bucket +with each entry’s color field populated.

  • +
+
+
Returns:
+

Mapping from color name to a list of run info dicts of shape:

+
{"paragraph": int, "run": int, "text": str, "color": str}
+
+
+

+
+
Return type:
+

Dict[str, List[Dict[str, Any]]]

+
+
+
+

Examples

+
>>> from scitex_msword.highlights import extract_highlights
+>>> by_color = extract_highlights(doc)
+>>> by_color.get("turquoise", [])
+[{'paragraph': 3, 'run': 0, 'text': '...', 'color': 'turquoise'}]
+
+
+
+
+ +
+
+scitex.msword.clear_highlights(document, colors=None)[source]
+

Remove highlights from all runs (optionally only for the listed colors).

+
+
Parameters:
+
    +
  • document (Document) – Document to mutate in place.

  • +
  • colors (Optional[Iterable[str]]) – If provided, only runs with one of these highlight colors are +cleared. If None (default), every highlighted run is cleared.

  • +
+
+
Returns:
+

The same Document object, mutated.

+
+
Return type:
+

Document

+
+
+
+ +
+
+scitex.msword.preserve_bold_tokens(document, tokens, *, font_name='MS Gothic', case_sensitive=True)[source]
+

Walk every paragraph in document and bold-emphasize each token hit.

+

Wherever a token appears inside paragraph text, the paragraph’s runs +are split so that the token sits in its own run with bold=True +and font.name = font_name (Latin + East-Asian + complex script +slots are all set so Japanese text picks up MS Gothic in Word).

+
+
Parameters:
+
    +
  • document (Document) – The Document to mutate in place.

  • +
  • tokens (Sequence[str]) – Strings to emphasize. Empty strings are ignored. Longer tokens +take precedence on overlapping matches.

  • +
  • font_name (str) – Font face applied to matched tokens.

  • +
  • case_sensitive (bool) – If False, matching is case-insensitive.

  • +
+
+
Returns:
+

The same Document object, mutated.

+
+
Return type:
+

Document

+
+
+
+

Notes

+

This implementation rewrites all runs of a paragraph when at least +one token hits; paragraphs without hits are left untouched. The +original surrounding format (italic, underline, size, highlight) is +captured from the first run before rebuilding — if you need +finer-grained preservation, run preserve_bold_tokens() before +other run-level edits.

+
+
+

Examples

+
>>> from scitex_msword.bold import preserve_bold_tokens
+>>> preserve_bold_tokens(doc, tokens=["BOOST", "JST"])
+
+
+
+
+ +
+
+scitex.msword.extract_comments(document)[source]
+

Extract Word comments from a .docx file or open Document.

+
+
Parameters:
+

document (Union[str, Path, Document]) – Path to the .docx or an already-open Document.

+
+
Returns:
+

One entry per comment:

+
{"id": int | str,
+ "author": str,
+ "date": str,           # ISO timestamp string, may be empty
+ "text": str,           # comment body
+ "anchor_text": str,    # text the comment is anchored to
+ "paragraph_range": [start, end]}
+
+
+

anchor_text and paragraph_range default to "" and +[None, None] when no in-document anchor can be located.

+

+
+
Return type:
+

List[Dict[str, Any]]

+
+
+
+

Examples

+
>>> from scitex_msword.comments import extract_comments
+>>> comments = extract_comments("boost-v16.docx")
+>>> [c["text"] for c in comments]
+['Please rephrase this', 'REPLACE: Use the new wording']
+
+
+
+
+ +
+
+scitex.msword.apply_comments_as_edits(document, *, comments=None, grammar='replace')[source]
+

Apply comments to the document body using a narrow grammar.

+

Only the REPLACE: grammar is currently supported, i.e. a comment +whose body matches r"^\s*REPLACE\s*:\s*(.+?)\s*$" is interpreted +as “replace this comment’s anchor text with the trailing payload”. +Other comments are ignored.

+
+
Parameters:
+
    +
  • document (Document) – The Document to mutate in place.

  • +
  • comments (Optional[List[Dict[str, Any]]]) – Pre-extracted comments (as returned by extract_comments()). +If None, the comments are read from document directly.

  • +
  • grammar (str) – Reserved for future expansion. Currently only "replace" +is recognised.

  • +
+
+
Returns:
+

Summary: {"applied": int, "skipped": int, "details": [...]}.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+

Examples

+
>>> from scitex_msword.comments import apply_comments_as_edits
+>>> summary = apply_comments_as_edits(doc)
+>>> summary["applied"]
+2
+
+
+
+
+ +
+
+scitex.msword.enable_track_changes(document, enabled=True)[source]
+

Toggle Word’s “Track Changes” switch on the document.

+

Inserts <w:trackChanges/> into word/settings.xml when +enabled=True (idempotent) or removes it when enabled=False.

+
+
Parameters:
+
    +
  • document (Document) – The Document to mutate in place.

  • +
  • enabled (bool) – True keeps a single <w:trackChanges/> element present; +False removes any such elements.

  • +
+
+
Returns:
+

The same Document object (chainable).

+
+
Return type:
+

Document

+
+
+
+ +
+
+scitex.msword.is_track_changes_enabled(document)[source]
+

Return True iff <w:trackChanges/> is present in settings.xml.

+
+
Return type:
+

bool

+
+
+
+ +
+
+scitex.msword.wrap_as_tracked_insertion(paragraph, runs, author='agent', date=None, w_id=None)[source]
+

Wrap the given runs of paragraph in <w:ins> revision blocks.

+

Word renders the wrapped content as “inserted by <author>” and +surfaces it as an accept/reject-able revision.

+
+
Parameters:
+
    +
  • paragraph (Paragraph) – Paragraph that owns the runs to wrap.

  • +
  • runs (Sequence[Any]) – Runs to wrap, by Run object or by 0-based index.

  • +
  • author (str) – Recorded in w:author.

  • +
  • date (Optional[str]) – ISO-8601 string for w:date; defaults to now(UTC).

  • +
  • w_id (Optional[int]) – Explicit revision id; auto-assigned (max+1) when None.

  • +
+
+
Returns:
+

Newly created <w:ins> lxml elements.

+
+
Return type:
+

List[Any]

+
+
+
+ +
+
+scitex.msword.wrap_as_tracked_deletion(paragraph, runs, author='agent', date=None, w_id=None)[source]
+

Wrap the given runs of paragraph in <w:del> revision blocks.

+

Each wrapped run’s <w:t> children are also retagged as +<w:delText> so Word renders the deletion with strike-through.

+
+
Parameters:
+
    +
  • paragraph (Paragraph) – Paragraph that owns the runs to wrap.

  • +
  • runs (Sequence[Any]) – Runs to wrap, by Run object or by 0-based index.

  • +
  • author (str) – Recorded in w:author.

  • +
  • date (Optional[str]) – ISO-8601 string for w:date; defaults to now(UTC).

  • +
  • w_id (Optional[int]) – Explicit revision id; auto-assigned (max+1) when None.

  • +
+
+
Returns:
+

Newly created <w:del> lxml elements.

+
+
Return type:
+

List[Any]

+
+
+
+ +
+
+scitex.msword.extract_tracked_changes(document)[source]
+

Return every <w:ins> / <w:del> revision as a structured dict.

+
+
Parameters:
+

document (Document) – The Document to scan.

+
+
Returns:
+

Each entry is shaped as:

+
{"type": "insert" | "delete",
+ "paragraph_idx": int,
+ "author": str,
+ "date": str,
+ "id": str,
+ "text": str}
+
+
+

+
+
Return type:
+

List[dict]

+
+
+
+ +
+
+scitex.msword.accept_all_tracked_changes(document)[source]
+

Accept all tracked changes — equivalent to Word’s “Accept All”.

+

<w:ins> wrappers are unwrapped (content remains); <w:del> +wrappers and their contents are removed.

+
+
Parameters:
+

document (Document) – The Document to mutate in place.

+
+
Returns:
+

The same Document, mutated.

+
+
Return type:
+

Document

+
+
+
+ +
+
+scitex.msword.reject_all_tracked_changes(document)[source]
+

Reject all tracked changes — equivalent to Word’s “Reject All”.

+

<w:ins> wrappers and contents are removed; <w:del> wrappers +are unwrapped and their <w:delText> children retagged back to +<w:t> so the original text is restored.

+
+
Parameters:
+

document (Document) – The Document to mutate in place.

+
+
Returns:
+

The same Document, mutated.

+
+
Return type:
+

Document

+
+
+
+ +
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/nn.html b/src/scitex/_sphinx_html/source/api/nn.html new file mode 100644 index 000000000..9cfd2fe02 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/nn.html @@ -0,0 +1,750 @@ + + + + + + + + + NN Module (stx.nn) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

NN Module (stx.nn)

+

PyTorch neural network layers for signal processing and neuroscience +applications.

+
+

Quick Reference

+
import scitex as stx
+import torch
+
+# Bandpass filtering as a differentiable layer
+bpf = stx.nn.BandPassFilter(
+    bands=[[4, 8], [8, 13], [13, 30]],  # theta, alpha, beta
+    fs=256, seq_len=1024
+)
+filtered = bpf(signal)  # (batch, channels, 3, 1024)
+
+# Power spectral density
+psd = stx.nn.PSD(sample_rate=256)
+power, freqs = psd(signal)
+
+# Phase-amplitude coupling
+pac = stx.nn.PAC(seq_len=1024, fs=256)
+coupling = pac(signal)
+
+
+
+
+

Signal Processing Layers

+

Filtering (all differentiable):

+
    +
  • BandPassFilter(bands, fs, seq_len) – Multi-band frequency filtering

  • +
  • BandStopFilter(bands, fs, seq_len) – Reject frequency bands

  • +
  • LowPassFilter(cutoffs_hz, fs, seq_len) – Anti-aliasing / smoothing

  • +
  • HighPassFilter(cutoffs_hz, fs, seq_len) – High-frequency emphasis

  • +
  • GaussianFilter(sigma) – Gaussian kernel smoothing

  • +
  • DifferentiableBandPassFilter(...) – Learnable bandpass parameters

  • +
+

Spectral Analysis:

+
    +
  • Spectrogram(sampling_rate, n_fft) – STFT-based magnitude spectrogram

  • +
  • PSD(sample_rate, prob) – FFT-based power spectral density

  • +
  • Wavelet(samp_rate, freq_scale) – Continuous wavelet transform

  • +
+

Phase & Coupling:

+
    +
  • Hilbert(seq_len) – Analytic signal (phase + amplitude)

  • +
  • ModulationIndex(n_bins) – Phase-amplitude coupling metric

  • +
  • PAC(seq_len, fs, ...) – Complete PAC analysis pipeline

  • +
+
+
+

Channel Manipulation

+
    +
  • SwapChannels() – Random channel permutation (training augmentation)

  • +
  • DropoutChannels(dropout) – Drop entire channels

  • +
  • ChannelGainChanger(n_chs) – Learnable per-channel scaling

  • +
  • FreqGainChanger(n_bands, fs) – Learnable per-band scaling

  • +
+
+
+

Attention & Shape

+
    +
  • SpatialAttention(n_chs_in) – Adaptive channel weighting

  • +
  • TransposeLayer(axis1, axis2) – Dimension permutation

  • +
  • AxiswiseDropout(dropout_prob, dim) – Drop entire axis

  • +
+
+
+

Architectures

+
    +
  • ResNet1D(n_chs, n_out, n_blks) – 1D residual network

  • +
  • BNet / BNet_Res – Multi-head EEG classifier

  • +
  • MNet1000 – 2D CNN feature extractor

  • +
+
+
+

API Reference

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/notebook.html b/src/scitex/_sphinx_html/source/api/notebook.html new file mode 100644 index 000000000..71e58dc92 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/notebook.html @@ -0,0 +1,915 @@ + + + + + + + + + notebook Module (stx.notebook) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

notebook Module (stx.notebook)

+

SciTeX Notebook — Jupyter notebook verification and compilation.

+

Provides tools to verify, compile, convert, and check Jupyter notebooks +for reproducibility using the Clew verification system.

+

Key Concept: Notebooks can be executed in any cell order. SciTeX records +actual execution order via timestamps, then reconstructs the dependency +DAG afterward (“do what you want, organize later”).

+
+

Examples

+
>>> from scitex_notebook import verify_notebook, compile_notebook
+>>> results = verify_notebook("experiment.ipynb")
+>>> compiled = compile_notebook("experiment.ipynb")
+>>> print(compiled.to_mermaid())  # DAG visualization
+>>> print(compiled.to_script())   # DAG-ordered .py
+
+
+
+
+
+scitex.notebook.parse_notebook(path)[source]
+

Parse a .ipynb file and extract code cells.

+
+
Parameters:
+

path (Union[str, Path]) – Path to the .ipynb file.

+
+
Returns:
+

Code cells with keys: index, source, cell_id, cell_type.

+
+
Return type:
+

List[Dict]

+
+
+
+ +
+
+scitex.notebook.get_code_cells(path)[source]
+

Parse notebook and return only code cells.

+
+
Parameters:
+

path (Union[str, Path]) – Path to the .ipynb file.

+
+
Returns:
+

Code cells only.

+
+
Return type:
+

List[Dict]

+
+
+
+ +
+
+scitex.notebook.get_notebook_name(path)[source]
+

Return the notebook stem name without extension.

+
+
Return type:
+

str

+
+
+
+ +
+
+scitex.notebook.verify_notebook(path)[source]
+

Verify all clew sessions associated with a notebook.

+

Finds all runs in the clew DB whose metadata contains this notebook’s +path, then runs L1 (cache) verification on each.

+
+
Parameters:
+

path (Union[str, Path]) – Path to the .ipynb file.

+
+
Returns:
+

Verification results per session.

+
+
Return type:
+

List[Dict]

+
+
+
+ +
+
+scitex.notebook.check_notebook(path)[source]
+

Find cells with scitex.io calls not wrapped in @scitex.session.

+
+
Parameters:
+

path (Union[str, Path]) – Path to the .ipynb file.

+
+
Returns:
+

Cells with untracked IO: {index, has_load, has_save, has_session}.

+
+
Return type:
+

List[Dict]

+
+
+
+ +
+
+scitex.notebook.compile_notebook(path)[source]
+

Compile a notebook’s execution history into a DAG.

+

Queries the clew DB for all sessions associated with this notebook, +sorts by timestamp, and builds a dependency DAG based on shared +input/output files.

+
+
Parameters:
+

path (Union[str, Path]) – Path to the .ipynb file.

+
+
Returns:
+

Compiled execution history with DAG and execution order.

+
+
Return type:
+

CompiledNotebook

+
+
+
+ +
+
+class scitex.notebook.CompiledNotebook(notebook_path, execution_order=<factory>, dag=<factory>, runs=<factory>)[source]
+

Bases: object

+

Result of compiling a notebook’s execution history.

+
+
+notebook_path
+

Path to the source notebook.

+
+
Type:
+

str

+
+
+
+ +
+
+execution_order
+

Session IDs in actual execution order (by timestamp).

+
+
Type:
+

list of str

+
+
+
+ +
+
+dag
+

Adjacency list: {session_id: [dependent_session_ids]}.

+
+
Type:
+

dict

+
+
+
+ +
+
+runs
+

Run records sorted by execution time.

+
+
Type:
+

list of dict

+
+
+
+ +
+
+notebook_path: str
+
+ +
+
+execution_order: List[str]
+
+ +
+
+dag: Dict[str, List[str]]
+
+ +
+
+runs: List[Dict]
+
+ +
+
+to_script()[source]
+

Generate a .py script with sessions in DAG order.

+
+
Return type:
+

str

+
+
+
+ +
+
+to_mermaid()[source]
+

Generate a Mermaid DAG diagram of execution flow.

+
+
Return type:
+

str

+
+
+
+ +
+ +
+
+scitex.notebook.convert_notebook(path, output=None, order='cell', mode='per_cell')[source]
+

Convert a .ipynb notebook to a .py script with @stx.session.

+
+
Parameters:
+
    +
  • path (Union[str, Path]) – Path to the .ipynb file.

  • +
  • output (Union[str, Path, None]) – Output .py file path. If None, returns string only.

  • +
  • order (str) – Cell ordering: “cell” (notebook order) or “dag” (execution order +from clew DB timestamps).

  • +
  • mode (str) –

    Conversion mode: +- “per_cell”: Each code cell becomes a separate @stx.session function (default). +- “unified”: All cells merged into a single @stx.session main() function.

    +
    +

    Markdown cells become comments, imports are hoisted, and common +notebook patterns (plt.show, pd.read_csv, etc.) are converted to +SciTeX equivalents (stx.io.save/load).

    +
    +

  • +
+
+
Returns:
+

The generated Python script content.

+
+
Return type:
+

str

+
+
+
+ +
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/notification.html b/src/scitex/_sphinx_html/source/api/notification.html new file mode 100644 index 000000000..08f7e1992 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/notification.html @@ -0,0 +1,800 @@ + + + + + + + + + Notification Module (stx.notification) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Notification Module (stx.notification)

+

Multi-backend notification system for alerting researchers when +long-running tasks complete, errors occur, or results are ready.

+
+

Note

+

stx.notification delegates to the standalone +scitex-notification +package. Install with: pip install scitex-notification.

+
+
+

Supported Backends

+ ++++ + + + + + + + + + + + + + + + + + + + +

Backend

Description

desktop

Native desktop notifications (Linux notify-send, macOS, Windows)

email

Email via SMTP

slack

Slack messages via webhook or Bot API

webhook

Generic HTTP POST to any URL

+
+
+

Quick Start

+
import scitex as stx
+
+# Send a notification (uses default backend)
+stx.notification.send("Training complete! Accuracy: 95.2%")
+
+# Send to a specific backend
+stx.notification.send("Job finished", backend="slack")
+
+# List available backends
+stx.notification.list_backends()
+
+
+
+
+

Key Functions

+
+

send(message, backend=None, **kwargs)

+

Send a notification through one or more backends.

+
# Simple message
+stx.notification.send("Experiment finished successfully.")
+
+# With title
+stx.notification.send("95.2% accuracy achieved", title="Training Complete")
+
+# To a specific backend
+stx.notification.send("Results ready", backend="email")
+
+
+
+
+

config(**kwargs)

+

Configure notification backends. Settings persist across sessions.

+
# Configure Slack
+stx.notification.config(
+    slack_webhook="https://hooks.slack.com/services/...",
+)
+
+# Configure email
+stx.notification.config(
+    email_to="researcher@university.edu",
+    email_from="scitex@lab.org",
+    smtp_host="smtp.university.edu",
+)
+
+
+
+
+

list_backends()

+

List available and configured backends.

+
backends = stx.notification.list_backends()
+# [{'name': 'desktop', 'available': True, 'configured': True},
+#  {'name': 'slack', 'available': True, 'configured': False}, ...]
+
+
+
+
+
+

Integration with @stx.session

+

Enable automatic notifications when a session completes:

+
import scitex as stx
+
+@stx.session(notify=True)
+def main(CONFIG=stx.INJECTED):
+    """Long-running experiment."""
+    result = train_model()
+    return 0    # Sends "Session FINISHED_SUCCESS" notification
+
+if __name__ == "__main__":
+    main()
+
+
+
+
+

CLI Access

+
# Send a notification
+scitex notify send "Job complete"
+
+# Check backend status
+scitex notify backends
+
+# Configure a backend
+scitex notify config --slack-webhook "https://..."
+
+
+
+
+

API Reference

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/os.html b/src/scitex/_sphinx_html/source/api/os.html new file mode 100644 index 000000000..c21309a70 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/os.html @@ -0,0 +1,697 @@ + + + + + + + + + os Module (stx.os) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

os Module (stx.os)

+

scitex-os — host check + safe file mv (standalone, pure stdlib).

+
+
+scitex.os.check_host(keyword)[source]
+

Check if the current hostname contains the given keyword.

+
+ +
+
+scitex.os.is_host(keyword)
+

Check if the current hostname contains the given keyword.

+
+ +
+
+scitex.os.mv(src, tgt)[source]
+
+ +
+
+scitex.os.verify_host(keyword)[source]
+
+ +
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/parallel.html b/src/scitex/_sphinx_html/source/api/parallel.html new file mode 100644 index 000000000..dcba55a68 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/parallel.html @@ -0,0 +1,707 @@ + + + + + + + + + parallel Module (stx.parallel) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

parallel Module (stx.parallel)

+

SciTeX Parallel — thread/process pool parallel execution utilities.

+
+
+scitex.parallel.run(func, args_list, n_jobs=-1, desc='Processing')[source]
+

Runs function in parallel using ThreadPoolExecutor with tuple arguments.

+
+
Parameters:
+
    +
  • func (Callable) – Function to run in parallel

  • +
  • args_list (List[tuple]) – List of argument tuples, each tuple contains arguments for one function call

  • +
  • n_jobs (int) – Number of jobs to run in parallel. -1 means using all processors

  • +
  • desc (str) – Description for progress bar

  • +
+
+
Returns:
+

Results of parallel execution

+
+
Return type:
+

List[Any]

+
+
+
+

Examples

+
>>> def add(x, y):
+...     return x + y
+>>> args_list = [(1, 4), (2, 5), (3, 6)]
+>>> run(add, args_list)
+[5, 7, 9]
+
+
+
+
+ +
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/path.html b/src/scitex/_sphinx_html/source/api/path.html new file mode 100644 index 000000000..f802f6a27 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/path.html @@ -0,0 +1,1140 @@ + + + + + + + + + path Module (stx.path) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

path Module (stx.path)

+

scitex-path: Scientific project path utilities (find, split, symlink, versioning).

+
+
+scitex.path.clean(path_string)[source]
+

Cleans and normalizes a file system path string.

+
+

Example

+
>>> clean('/home/user/./folder/../file.txt')
+'/home/user/file.txt'
+>>> clean('path/./to//file.txt')
+'path/to/file.txt'
+>>> clean('path with spaces')
+'path_with_spaces'
+
+
+
+
+
Parameters:
+

path_string (str) – File path to clean

+
+
Returns:
+

Normalized path string

+
+
Return type:
+

str

+
+
+
+ +
+ +

Create a relative symbolic link.

+

This is a convenience wrapper around symlink() with relative=True.

+
+
Parameters:
+
    +
  • src (Union[str, Path]) – Source path (target of the symlink)

  • +
  • dst (Union[str, Path]) – Destination path (the symlink to create)

  • +
  • overwrite (bool) – If True, remove existing dst before creating symlink

  • +
+
+
Return type:
+

Path

+
+
Returns:
+

Path object of the created symlink

+
+
+
+ +
+
+scitex.path.find_dir(root_dir, exp)[source]
+

Find directories matching pattern.

+
+
Return type:
+

List[str]

+
+
+
+ +
+
+scitex.path.find_file(root_dir, exp)[source]
+

Find files matching pattern.

+
+
Return type:
+

List[str]

+
+
+
+ +
+
+scitex.path.find_git_root()[source]
+

Find the root directory of the current git repository.

+
+
Returns:
+

Path to the git repository root.

+
+
Return type:
+

str

+
+
+
+ +
+
+scitex.path.find_latest(dirname, fname, ext, version_prefix='_v')[source]
+

Find the latest versioned file in a directory.

+
+
Parameters:
+
    +
  • dirname (str) – Directory to search in.

  • +
  • fname (str) – Base filename without version number or extension.

  • +
  • ext (str) – File extension including the dot (e.g., ‘.txt’).

  • +
  • version_prefix (str, optional) – Prefix before the version number. Default is ‘_v’.

  • +
+
+
Returns:
+

Path to the latest versioned file, or None if not found.

+
+
Return type:
+

str or None

+
+
+
+ +
+ +

Find and optionally fix broken symbolic links.

+
+
Parameters:
+
    +
  • directory (Union[str, Path]) – Directory to search

  • +
  • recursive (bool) – If True, search recursively

  • +
  • remove (bool) – If True, remove broken symlinks

  • +
  • new_target (Union[str, Path, None]) – If provided, repoint broken symlinks to this target

  • +
+
+
Return type:
+

dict

+
+
Returns:
+

Dictionary with ‘found’, ‘fixed’, and ‘removed’ lists of paths

+
+
+
+ +
+
+scitex.path.get_data_path_from_a_package(package_str, resource)[source]
+

Get the path to a data file within a package.

+
+
Parameters:
+
    +
  • package_str (str) – The name of the package as a string.

  • +
  • resource (str) – The name of the resource file within the package’s data directory.

  • +
+
+
Returns:
+

The full path to the resource file.

+
+
Return type:
+

Path

+
+
Raises:
+
    +
  • ImportError – If the specified package cannot be found.

  • +
  • FileNotFoundError – If the resource file does not exist in the package’s data directory.

  • +
+
+
+
+ +
+
+scitex.path.get_spath(sfname, makedirs=False)
+

Create a save path based on the calling script’s location.

+
+
Parameters:
+
    +
  • sfname (Union[str, Path]) – The name of the file to be saved.

  • +
  • makedirs (bool) – If True, create the directory structure for the save path.

  • +
+
+
Returns:
+

The full save path for the file.

+
+
Return type:
+

str

+
+
+
+

Example

+
>>> spath = mk_spath('output.txt', makedirs=True)
+
+
+
+
+ +
+
+scitex.path.get_this_path(ipython_fake_path='/tmp/fake.py')
+

Get the path of the calling script.

+
+

Note

+

This function historically captures the caller’s filename via +inspect.stack()[1] but then returns this module’s __file__. +The tests codify that legacy behavior; do not change without +updating callers.

+
+
+
Return type:
+

str

+
+
+
+ +
+
+scitex.path.getsize(path)[source]
+

Get file size in bytes.

+
+
Parameters:
+

path (Union[str, Path]) – Path to file.

+
+
Returns:
+

File size in bytes, or math.nan if file doesn’t exist.

+
+
Return type:
+

Union[int, float]

+
+
Raises:
+

PermissionError – If the file cannot be accessed due to permissions.

+
+
+
+ +
+
+scitex.path.increment_version(dirname, fname, ext, version_prefix='_v')[source]
+

Generate the next version of a filename based on existing versioned files.

+
+
Parameters:
+
    +
  • dirname (str) – Directory to search in.

  • +
  • fname (str) – Base filename without version number or extension.

  • +
  • ext (str) – File extension including the dot (e.g., ‘.txt’).

  • +
  • version_prefix (str, optional) – Prefix before the version number. Default is ‘_v’.

  • +
+
+
Returns:
+

Full path for the next version of the file.

+
+
Return type:
+

str

+
+
+
+

Example

+
>>> increment_version('/path/to/dir', 'myfile', '.txt')
+'/path/to/dir/myfile_v001.txt'
+
+
+
+
+ +
+ +

Check if a path is a symbolic link.

+
+
Parameters:
+

path (Union[str, Path]) – Path to check

+
+
Return type:
+

bool

+
+
Returns:
+

True if path is a symlink, False otherwise

+
+
+
+ +
+ +

List all symbolic links in a directory.

+
+
Parameters:
+
    +
  • directory (Union[str, Path]) – Directory to search

  • +
  • recursive (bool) – If True, search recursively

  • +
+
+
Return type:
+

list[Path]

+
+
Returns:
+

List of Path objects for all symlinks found

+
+
+
+ +
+
+scitex.path.mk_spath(sfname, makedirs=False)[source]
+

Create a save path based on the calling script’s location.

+
+
Parameters:
+
    +
  • sfname (Union[str, Path]) – The name of the file to be saved.

  • +
  • makedirs (bool) – If True, create the directory structure for the save path.

  • +
+
+
Returns:
+

The full save path for the file.

+
+
Return type:
+

str

+
+
+
+

Example

+
>>> spath = mk_spath('output.txt', makedirs=True)
+
+
+
+
+ +
+ +

Return the path to which the symbolic link points.

+
+
Parameters:
+

path (Union[str, Path]) – Symlink path to read

+
+
Return type:
+

Path

+
+
Returns:
+

Path object pointing to the symlink target

+
+
Raises:
+

OSError – If path is not a symlink

+
+
+
+ +
+ +

Resolve all symbolic links in a path.

+
+
Parameters:
+

path (Union[str, Path]) – Path potentially containing symlinks

+
+
Return type:
+

Path

+
+
Returns:
+

Fully resolved absolute path

+
+
+
+ +
+
+scitex.path.split(fpath)[source]
+

Split a file path into directory, filename, and extension.

+
+
Parameters:
+

fpath (Union[str, Path]) – File path to split.

+
+
Returns:
+

(directory with trailing slash, filename without extension, extension)

+
+
Return type:
+

Tuple[str, str, str]

+
+
+
+

Example

+
>>> dirname, fname, ext = split('/path/to/file.txt')
+>>> dirname
+'/path/to/'
+>>> fname
+'file'
+>>> ext
+'.txt'
+
+
+
+
+ +
+ +

Create a symbolic link pointing to src named dst.

+
+
Parameters:
+
    +
  • src (Union[str, Path]) – Source path (target of the symlink)

  • +
  • dst (Union[str, Path]) – Destination path (the symlink to create)

  • +
  • overwrite (bool) – If True, remove existing dst before creating symlink

  • +
  • target_is_directory (Optional[bool]) – On Windows, specify if target is directory (auto-detected if None)

  • +
  • relative (bool) – If True, create relative symlink instead of absolute

  • +
+
+
Return type:
+

Path

+
+
Returns:
+

Path object of the created symlink

+
+
Raises:
+
+
+
+
+

Examples

+
>>> from scitex_path import symlink
+>>> # Create absolute symlink
+>>> symlink("/path/to/source", "/path/to/link")
+
+
+
>>> # Create relative symlink
+>>> symlink("../source", "link", relative=True)
+
+
+
>>> # Overwrite existing symlink
+>>> symlink("/path/to/new_source", "/path/to/link", overwrite=True)
+
+
+
+
+ +
+
+scitex.path.this_path(ipython_fake_path='/tmp/fake.py')[source]
+

Get the path of the calling script.

+
+

Note

+

This function historically captures the caller’s filename via +inspect.stack()[1] but then returns this module’s __file__. +The tests codify that legacy behavior; do not change without +updating callers.

+
+
+
Return type:
+

str

+
+
+
+ +
+ +

Remove a symbolic link.

+
+
Parameters:
+
    +
  • path (Union[str, Path]) – Symlink to remove

  • +
  • missing_ok (bool) – If True, don’t raise error if symlink doesn’t exist

  • +
+
+
Raises:
+
+
+
Return type:
+

None

+
+
+
+ +
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/pd.html b/src/scitex/_sphinx_html/source/api/pd.html new file mode 100644 index 000000000..d28c78829 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/pd.html @@ -0,0 +1,714 @@ + + + + + + + + + PD Module (stx.pd) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

PD Module (stx.pd)

+

Pandas DataFrame helper functions for common transformations.

+
+

Quick Reference

+
import scitex as stx
+import pandas as pd
+
+df = pd.DataFrame({
+    "subject": ["A", "A", "B", "B"],
+    "condition": ["ctrl", "exp", "ctrl", "exp"],
+    "score": [10, 15, 12, 18],
+})
+
+# Find individual (unique) values
+subjects = stx.pd.find_indi(df, "subject")
+
+# Convert to long format
+melted = stx.pd.melt_cols(df, id_vars=["subject"])
+
+# Merge columns
+merged = stx.pd.merge_cols(df, cols=["subject", "condition"], sep="_")
+
+# Force to DataFrame
+stx.pd.force_df({"a": [1, 2], "b": [3, 4]})
+
+
+
+
+

Available Functions

+
    +
  • find_indi(df, col) – Find unique individual identifiers

  • +
  • find_pval(df) – Find p-value columns in a DataFrame

  • +
  • force_df(data) – Convert any data to a DataFrame

  • +
  • melt_cols(df, id_vars) – Melt columns to long format

  • +
  • merge_cols(df, cols, sep) – Merge multiple columns into one

  • +
  • to_xyz(df, x, y, z) – Reshape to x, y, z pivot format

  • +
+
+
+

API Reference

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/plt.html b/src/scitex/_sphinx_html/source/api/plt.html new file mode 100644 index 000000000..68ac9d755 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/plt.html @@ -0,0 +1,1104 @@ + + + + + + + + + scitex.plt — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

scitex.plt

+
+

Figure and Axis Creation

+
+ +
+

Axis Methods

+

The following methods are available on ax objects returned by stx.plt.subplots().

+
+

Line Plots

+
fig, ax = stx.plt.subplots()
+
+
+
+
+ax.stx_line(values_1d, xx=None, track=True, id=None, **kwargs)
+

Plot a simple line from 1D array.

+
+
Parameters:
+
    +
  • values_1d – Y values

  • +
  • xx – Optional X values

  • +
  • track – Track data for CSV export

  • +
  • id – Identifier for tracking

  • +
+
+
+
+ +
+
+ax.stx_shaded_line(xs, ys_lower, ys_middle, ys_upper, color=None, label=None, track=True, id=None, **kwargs)
+

Plot a line with shaded area between bounds.

+
+
Parameters:
+
    +
  • xs – X values

  • +
  • ys_lower – Lower bound Y values

  • +
  • ys_middle – Middle line Y values

  • +
  • ys_upper – Upper bound Y values

  • +
+
+
+
+ +
+
+

Statistical Plots

+
+
+ax.stx_mean_std(values_2d, xx=None, sd=1, track=True, id=None, **kwargs)
+

Plot mean line with standard deviation shading.

+
+
Parameters:
+
    +
  • values_2d – 2D array (samples x timepoints)

  • +
  • xx – Optional X values

  • +
  • sd – Number of standard deviations for shading

  • +
+
+
+
+ +
+
+ax.stx_mean_ci(values_2d, xx=None, perc=95, track=True, id=None, **kwargs)
+

Plot mean line with confidence interval shading.

+
+
Parameters:
+
    +
  • values_2d – 2D array (samples x timepoints)

  • +
  • xx – Optional X values

  • +
  • perc – Confidence interval percentage (default: 95)

  • +
+
+
+
+ +
+
+ax.stx_median_iqr(values_2d, xx=None, track=True, id=None, **kwargs)
+

Plot median line with interquartile range shading.

+
+
Parameters:
+
    +
  • values_2d – 2D array (samples x timepoints)

  • +
  • xx – Optional X values

  • +
+
+
+
+ +
+
+ax.stx_errorbar(x, y, yerr=None, xerr=None, track=True, id=None, **kwargs)
+

Error bar plot with scitex styling.

+
+
Parameters:
+
    +
  • x – X coordinates

  • +
  • y – Y coordinates

  • +
  • yerr – Y error values

  • +
  • xerr – X error values

  • +
+
+
+
+ +
+
+

Distribution Plots

+
+
+ax.stx_kde(values_1d, cumulative=False, fill=False, track=True, id=None, **kwargs)
+

Plot kernel density estimate.

+
+
Parameters:
+
    +
  • values_1d – 1D array of values

  • +
  • cumulative – Plot cumulative distribution

  • +
  • fill – Fill under curve

  • +
+
+
+
+ +
+
+ax.stx_ecdf(values_1d, track=True, id=None, **kwargs)
+

Plot empirical cumulative distribution function.

+
+
Parameters:
+

values_1d – 1D array of values

+
+
+
+ +
+
+ax.hist(x, bins=10, range=None, align_bins=True, track=True, id=None, **kwargs)
+

Plot histogram with bin alignment support.

+
+
Parameters:
+
    +
  • x – Input data

  • +
  • bins – Number of bins or bin edges

  • +
  • align_bins – Align bins with other histograms on same axis

  • +
+
+
+
+ +
+
+

Categorical Plots

+
+
+ax.stx_box(values_list, colors=None, track=True, id=None, **kwargs)
+

Boxplot with scitex styling.

+
+
Parameters:
+
    +
  • values_list – List of arrays for each box

  • +
  • colors – Optional colors for boxes

  • +
+
+
+
+ +
+
+ax.stx_violin(values_list, x=None, y=None, hue=None, labels=None, colors=None, half=False, track=True, id=None, **kwargs)
+

Violin plot with scitex styling.

+
+
Parameters:
+
    +
  • values_list – List of arrays or DataFrame

  • +
  • half – Show half violins

  • +
+
+
+
+ +
+
+

Scatter Plots

+
+
+ax.stx_scatter(x, y, track=True, id=None, **kwargs)
+

Scatter plot with scitex styling.

+
+
Parameters:
+
    +
  • x – X coordinates

  • +
  • y – Y coordinates

  • +
+
+
+
+ +
+
+ax.stx_scatter_hist(x, y, hist_bins=20, scatter_alpha=0.6, track=True, id=None, **kwargs)
+

Scatter plot with marginal histograms.

+
+
Parameters:
+
    +
  • x – X coordinates

  • +
  • y – Y coordinates

  • +
  • hist_bins – Number of histogram bins

  • +
+
+
+
+ +
+
+

Bar Plots

+
+
+ax.stx_bar(x, height, track=True, id=None, **kwargs)
+

Vertical bar plot with scitex styling.

+
+
Parameters:
+
    +
  • x – X coordinates

  • +
  • height – Bar heights

  • +
+
+
+
+ +
+
+ax.stx_barh(y, width, track=True, id=None, **kwargs)
+

Horizontal bar plot with scitex styling.

+
+
Parameters:
+
    +
  • y – Y coordinates

  • +
  • width – Bar widths

  • +
+
+
+
+ +
+
+

Heatmaps

+
+
+ax.stx_heatmap(values_2d, x_labels=None, y_labels=None, cmap='viridis', cbar_label='', show_annot=True, track=True, id=None, **kwargs)
+

Plot annotated heatmap.

+
+
Parameters:
+
    +
  • values_2d – 2D array

  • +
  • x_labels – Column labels

  • +
  • y_labels – Row labels

  • +
  • show_annot – Show value annotations

  • +
+
+
+
+ +
+
+ax.stx_conf_mat(conf_mat_2d, x_labels=None, y_labels=None, title='Confusion Matrix', track=True, id=None, **kwargs)
+

Plot confusion matrix.

+
+
Parameters:
+
    +
  • conf_mat_2d – 2D confusion matrix array

  • +
  • x_labels – Predicted class labels

  • +
  • y_labels – True class labels

  • +
+
+
+
+ +
+
+ax.stx_image(arr_2d, track=True, id=None, **kwargs)
+

Display 2D array as image.

+
+
Parameters:
+

arr_2d – 2D array

+
+
+
+ +
+
+

Special Plots

+
+
+ax.stx_raster(spike_times_list, time=None, labels=None, colors=None, track=True, id=None, **kwargs)
+

Plot spike raster.

+
+
Parameters:
+
    +
  • spike_times_list – List of spike time arrays per neuron

  • +
  • time – Optional time array

  • +
+
+
+
+ +
+
+ax.stx_fillv(starts_1d, ends_1d, color='red', alpha=0.2, track=True, id=None, **kwargs)
+

Fill vertical regions.

+
+
Parameters:
+
    +
  • starts_1d – Start positions

  • +
  • ends_1d – End positions

  • +
+
+
+
+ +
+
+ax.stx_rectangle(xx, yy, width, height, track=True, id=None, **kwargs)
+

Draw rectangle.

+
+
Parameters:
+
    +
  • xx – X position

  • +
  • yy – Y position

  • +
  • width – Rectangle width

  • +
  • height – Rectangle height

  • +
+
+
+
+ +
+
+ax.stx_joyplot(arrays, track=True, id=None, **kwargs)
+

Plot joyplot (ridgeline plot).

+
+
Parameters:
+

arrays – List of arrays

+
+
+
+ +
+
+

Seaborn Wrappers

+
+
+ax.sns_barplot(data=None, x=None, y=None, track=True, id=None, **kwargs)
+
+ +
+
+ax.sns_boxplot(data=None, x=None, y=None, strip=False, track=True, id=None, **kwargs)
+
+ +
+
+ax.sns_violinplot(data=None, x=None, y=None, track=True, id=None, **kwargs)
+
+ +
+
+ax.sns_stripplot(data=None, x=None, y=None, track=True, id=None, **kwargs)
+
+ +
+
+ax.sns_swarmplot(data=None, x=None, y=None, track=True, id=None, **kwargs)
+
+ +
+
+ax.sns_heatmap(*args, xyz=False, track=True, id=None, **kwargs)
+
+ +
+
+ax.sns_histplot(data=None, x=None, y=None, bins=10, track=True, id=None, **kwargs)
+
+ +
+
+ax.sns_kdeplot(data=None, x=None, y=None, track=True, id=None, **kwargs)
+
+ +
+
+ax.sns_scatterplot(data=None, x=None, y=None, track=True, id=None, **kwargs)
+
+ +
+
+ax.sns_lineplot(data=None, x=None, y=None, track=True, id=None, **kwargs)
+
+ +
+
+ax.sns_jointplot(*args, track=True, id=None, **kwargs)
+
+ +
+
+ax.sns_pairplot(*args, track=True, id=None, **kwargs)
+
+ +
+
+

Axis Utilities

+
+
+ax.set_xyt(x=None, y=None, t=None)
+

Set xlabel, ylabel, and title in one call.

+
+ +
+
+ax.hide_spines(*spines)
+

Hide specified spines (top, right, bottom, left).

+
+ +
+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/project.html b/src/scitex/_sphinx_html/source/api/project.html new file mode 100644 index 000000000..7f8275001 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/project.html @@ -0,0 +1,674 @@ + + + + + + + + + project Module (stx.project) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

project Module (stx.project)

+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/repro.html b/src/scitex/_sphinx_html/source/api/repro.html new file mode 100644 index 000000000..752d49d19 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/repro.html @@ -0,0 +1,1194 @@ + + + + + + + + + Repro Module (stx.repro) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Repro Module (stx.repro)

+

Reproducibility utilities: random state management, ID generation, +timestamps, and array hashing.

+
+

Quick Reference

+
import scitex as stx
+
+# Fix all random seeds (numpy, torch, random, ...)
+rng = stx.repro.get()           # Global manager (seed=42)
+rng = stx.repro.reset(seed=123) # Reset with new seed
+
+# Named generators for deterministic results
+data_gen = rng.get_np_generator("data")
+data = data_gen.random(100)  # Same seed+name = same result
+
+# Unique identifiers
+stx.repro.gen_id()
+# → "2026Y-02M-13D-14h30m15s_a3Bc9xY2"
+
+stx.repro.gen_timestamp()
+# → "2026-0213-1430"
+
+# Verify reproducibility
+rng.verify(data, "train_data")  # First: caches hash
+rng.verify(data, "train_data")  # Later: verifies match
+
+
+
+
+

RandomStateManager

+

Central class for managing random states across libraries.

+
rng = stx.repro.RandomStateManager(seed=42)
+
+# Named generators (same name + seed = deterministic)
+np_gen = rng.get_np_generator("experiment")
+torch_gen = rng.get_torch_generator("model")
+
+# Checkpoint and restore
+rng.checkpoint("before_training")
+rng.restore("before_training.pkl")
+
+# Temporary seed change
+with rng.temporary_seed(999):
+    noise = rng.get_np_generator("noise").random(10)
+
+
+

Automatically fixes seeds for: random, numpy, torch (+ CUDA), +tensorflow, jax.

+
+
+

Available Functions

+
    +
  • get(verbose) – Get or create global RandomStateManager singleton

  • +
  • reset(seed, verbose) – Reset global instance with new seed

  • +
  • fix_seeds(seed, ...) – Legacy function (use RandomStateManager instead)

  • +
  • gen_id(time_format, N) – Generate unique timestamp + random ID

  • +
  • gen_timestamp() – Generate timestamp string for file naming

  • +
  • hash_array(array_data) – SHA256 hash of numpy array (16 chars)

  • +
+
+
+

API Reference

+

scitex-repro — Reproducibility utilities for scientific computing.

+

Provides tools for reproducible scientific computing: +- Random state management (RandomStateManager) +- ID generation (gen_ID) +- Timestamp generation (gen_timestamp) +- Array hashing (hash_array)

+
+
+scitex.repro.gen_ID(time_format='%YY-%mM-%dD-%Hh%Mm%Ss', N=8, *, now_fn=None)
+

Generate a unique identifier with timestamp and random characters.

+

Creates a unique ID by combining a formatted timestamp with random +alphanumeric characters. Useful for creating unique experiment IDs, +run identifiers, or temporary file names.

+
+
Parameters:
+
    +
  • time_format (str, optional) – Format string for timestamp portion. Default is “%YY-%mM-%dD-%Hh%Mm%Ss” +which produces “2025Y-05M-31D-12h30m45s” format.

  • +
  • N (int, optional) – Number of random characters to append. Default is 8.

  • +
  • now_fn (callable, optional) – Zero-argument callable returning a datetime-like object with +.strftime(). Defaults to datetime.now. Injection point for +deterministic tests — pass a fake that returns a fixed datetime +instead of mocking datetime globally.

  • +
+
+
Returns:
+

Unique identifier in format “{timestamp}_{random_chars}”

+
+
Return type:
+

str

+
+
+
+

Examples

+
>>> id1 = gen_id()
+>>> print(id1)
+'2025Y-05M-31D-12h30m45s_a3Bc9xY2'
+
+
+
>>> id2 = gen_id(time_format="%Y%m%d", N=4)
+>>> print(id2)
+'20250531_xY9a'
+
+
+
>>> # For experiment tracking
+>>> exp_id = gen_id()
+>>> save_path = f"results/experiment_{exp_id}.pkl"
+
+
+
+
+ +
+
+scitex.repro.gen_id(time_format='%YY-%mM-%dD-%Hh%Mm%Ss', N=8, *, now_fn=None)[source]
+

Generate a unique identifier with timestamp and random characters.

+

Creates a unique ID by combining a formatted timestamp with random +alphanumeric characters. Useful for creating unique experiment IDs, +run identifiers, or temporary file names.

+
+
Parameters:
+
    +
  • time_format (str, optional) – Format string for timestamp portion. Default is “%YY-%mM-%dD-%Hh%Mm%Ss” +which produces “2025Y-05M-31D-12h30m45s” format.

  • +
  • N (int, optional) – Number of random characters to append. Default is 8.

  • +
  • now_fn (callable, optional) – Zero-argument callable returning a datetime-like object with +.strftime(). Defaults to datetime.now. Injection point for +deterministic tests — pass a fake that returns a fixed datetime +instead of mocking datetime globally.

  • +
+
+
Returns:
+

Unique identifier in format “{timestamp}_{random_chars}”

+
+
Return type:
+

str

+
+
+
+

Examples

+
>>> id1 = gen_id()
+>>> print(id1)
+'2025Y-05M-31D-12h30m45s_a3Bc9xY2'
+
+
+
>>> id2 = gen_id(time_format="%Y%m%d", N=4)
+>>> print(id2)
+'20250531_xY9a'
+
+
+
>>> # For experiment tracking
+>>> exp_id = gen_id()
+>>> save_path = f"results/experiment_{exp_id}.pkl"
+
+
+
+
+ +
+
+scitex.repro.gen_timestamp(*, now_fn=None)[source]
+

Generate a timestamp string for file naming.

+

Returns a timestamp in the format YYYY-MMDD-HHMM, suitable for +creating unique filenames or version identifiers.

+
+
Parameters:
+

now_fn (callable, optional) – Zero-argument callable returning a datetime-like object with +.strftime(). Defaults to datetime.now. Injection point for +deterministic tests — pass a fake that returns a fixed datetime +instead of mocking datetime globally.

+
+
Returns:
+

Timestamp string in format “YYYY-MMDD-HHMM”

+
+
Return type:
+

str

+
+
+
+

Examples

+
>>> timestamp = gen_timestamp()
+>>> print(timestamp)
+'2025-0531-1230'
+
+
+
>>> filename = f"experiment_{gen_timestamp()}.csv"
+>>> print(filename)
+'experiment_2025-0531-1230.csv'
+
+
+
+
+ +
+
+scitex.repro.timestamp(*, now_fn=None)
+

Generate a timestamp string for file naming.

+

Returns a timestamp in the format YYYY-MMDD-HHMM, suitable for +creating unique filenames or version identifiers.

+
+
Parameters:
+

now_fn (callable, optional) – Zero-argument callable returning a datetime-like object with +.strftime(). Defaults to datetime.now. Injection point for +deterministic tests — pass a fake that returns a fixed datetime +instead of mocking datetime globally.

+
+
Returns:
+

Timestamp string in format “YYYY-MMDD-HHMM”

+
+
Return type:
+

str

+
+
+
+

Examples

+
>>> timestamp = gen_timestamp()
+>>> print(timestamp)
+'2025-0531-1230'
+
+
+
>>> filename = f"experiment_{gen_timestamp()}.csv"
+>>> print(filename)
+'experiment_2025-0531-1230.csv'
+
+
+
+
+ +
+
+scitex.repro.hash_array(array_data)[source]
+

Generate hash for array data.

+

Creates a deterministic hash for numpy arrays, useful for +verifying data integrity and reproducibility.

+
+
Parameters:
+

array_data (ndarray) – Array to hash

+
+
Returns:
+

16-character hash string

+
+
Return type:
+

str

+
+
+
+

Examples

+
>>> import numpy as np
+>>> data = np.array([1, 2, 3, 4, 5])
+>>> hash1 = hash_array(data)
+>>> hash2 = hash_array(data)
+>>> hash1 == hash2
+True
+
+
+
+
+ +
+
+class scitex.repro.RandomStateManager(seed=42, verbose=False)[source]
+

Simple, robust random state manager for scientific computing.

+
+

Examples

+
>>> from scitex_repro import RandomStateManager
+>>>
+>>> # Method 1: Direct usage
+>>> rng = RandomStateManager(seed=42)
+>>> data = rng("data").random(100)
+>>>
+>>> # Verify reproducibility
+>>> rng.verify(data, "my_data")
+
+
+
+
+
+__init__(seed=42, verbose=False)[source]
+

Initialize with automatic module detection.

+
+ +
+
+get_np_generator(name)[source]
+

Get or create a named NumPy random generator.

+
+
Parameters:
+

name (str) – Generator name (e.g., “data”, “model”, “augment”)

+
+
Returns:
+

Independent NumPy random generator

+
+
Return type:
+

numpy.random.Generator

+
+
+
+

Examples

+
>>> rng = RandomStateManager(42)
+>>> gen = rng.get_np_generator("data")
+>>> values = gen.random(100)
+>>> perm = gen.permutation(100)
+
+
+
+
+ +
+
+__call__(name, verbose=None)[source]
+

Get or create a named NumPy random generator.

+

This is a backward compatibility wrapper for get_np_generator(). +Consider using get_np_generator() directly for clarity.

+
+
Parameters:
+
    +
  • name (str) – Generator name

  • +
  • verbose (bool) – Whether to show deprecation warning

  • +
+
+
Returns:
+

NumPy random generator with deterministic seed

+
+
Return type:
+

numpy.random.Generator

+
+
+
+ +
+
+verify(obj, name=None, verbose=True)[source]
+

Verify object matches cached hash (detects broken reproducibility).

+

First call: caches the object’s hash +Later calls: verifies object matches cached hash

+
+
Parameters:
+
    +
  • obj (Any) – Object to verify (array, tensor, data, model weights, etc.) +Supports: numpy arrays, torch tensors, tf tensors, jax arrays, +lists, dicts, pandas dataframes, and basic types

  • +
  • name (str) – Cache name. Auto-generated if not provided.

  • +
+
+
Returns:
+

True if matches cache (or first call), False if different

+
+
Return type:
+

bool

+
+
+
+

Examples

+
>>> data = generate_data()
+>>> rng.verify(data, "train_data")  # First run: caches
+>>> # Next run:
+>>> rng.verify(data, "train_data")  # Verifies match
+
+
+
+
+ +
+
+checkpoint(name='checkpoint')[source]
+

Save current state of all generators.

+
+ +
+
+restore(checkpoint)[source]
+

Restore from checkpoint.

+
+ +
+
+temporary_seed(seed)[source]
+

Context manager for temporary seed change.

+
+ +
+
+get_sklearn_random_state(name)[source]
+

Get a random state for scikit-learn.

+

Scikit-learn uses integers for random_state parameter.

+
+
Parameters:
+

name (str) – Generator name

+
+
Returns:
+

Random state integer for sklearn

+
+
Return type:
+

int

+
+
+
+

Examples

+
>>> rng = RandomStateManager(42)
+>>> from sklearn.model_selection import train_test_split
+>>> X_train, X_test = train_test_split(
+...     X, test_size=0.2,
+...     random_state=rng.get_sklearn_random_state("split")
+... )
+
+
+
+
+ +
+
+get_torch_generator(name)[source]
+

Get or create a named PyTorch generator.

+
+
Parameters:
+

name (str) – Generator name

+
+
Returns:
+

PyTorch generator with deterministic seed

+
+
Return type:
+

torch.Generator

+
+
+
+

Examples

+
>>> rng = RandomStateManager(42)
+>>> gen = rng.get_torch_generator("model")
+>>> torch.randn(5, 5, generator=gen)
+
+
+
+
+ +
+
+get_generator(name)[source]
+

Alias for get_np_generator for compatibility.

+
+ +
+
+clear_cache(patterns=None)[source]
+

Clear verification cache files.

+
+
Parameters:
+

patterns (str | list[str]) – Specific cache patterns to clear. If None, clears all.

+
+
Returns:
+

Number of cache files removed

+
+
Return type:
+

int

+
+
+
+ +
+ +
+
+scitex.repro.get(verbose=False)[source]
+

Get or create the global RandomStateManager instance.

+
+
Parameters:
+

verbose (bool) – Whether to print status messages (default: False)

+
+
Returns:
+

Global instance

+
+
Return type:
+

RandomStateManager

+
+
+
+

Examples

+
>>> from scitex_repro import get
+>>> rng = get()
+>>> data = rng("data").random(100)
+
+
+
+
+ +
+
+scitex.repro.reset(seed=42, verbose=False)[source]
+

Reset global RandomStateManager with new seed.

+
+
Parameters:
+
    +
  • seed (int) – New seed value

  • +
  • verbose (bool) – Whether to print status messages (default: False)

  • +
+
+
Returns:
+

New global instance

+
+
Return type:
+

RandomStateManager

+
+
+
+

Examples

+
>>> from scitex_repro import reset
+>>> rng = reset(seed=123)
+
+
+
+
+ +
+
+scitex.repro.fix_seeds(seed=42, os=True, random=True, np=True, torch=True, tf=False, jax=False, verbose=False, **kwargs)[source]
+

Deprecated: Use RandomStateManager instead.

+

This function maintains backward compatibility with the old fix_seeds API.

+
+ +
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/resource.html b/src/scitex/_sphinx_html/source/api/resource.html new file mode 100644 index 000000000..cf9bf6013 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/resource.html @@ -0,0 +1,888 @@ + + + + + + + + + resource Module (stx.resource) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

resource Module (stx.resource)

+

scitex-resource — system resource info, monitoring, RAM limit (standalone).

+

Public API is loaded lazily via PEP 562 __getattr__ so import +scitex_resource stays fast (<100ms cold-start) — Click runs the CLI +program once per Tab press for completion, and pulling in +matplotlib/psutil/yaml at top-level made that path ~2s slow.

+
+
+scitex.resource.get_host_config()[source]
+

Return the host: block from config (falls back to machine:).

+

If a config file declares only the legacy machine: key, a +one-time DeprecationWarning is emitted and that block is returned.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+scitex.resource.get_host_name()[source]
+

Return the canonical host name.

+

See module docstring for the resolution cascade. Always returns a +non-empty string — the short hostname is the last-resort fallback.

+
+
Return type:
+

str

+
+
+
+ +
+
+scitex.resource.load_config()[source]
+

Return the merged config dict — project file overrides user file.

+

Empty dict if no config files exist or PyYAML isn’t installed.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+scitex.resource.get_machine_config()[source]
+

Deprecated alias for get_host_config().

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+scitex.resource.get_machine_name()[source]
+

Deprecated alias for get_host_name().

+
+
Return type:
+

str

+
+
+
+ +
+
+scitex.resource.get_metrics(gpu=True)[source]
+

Return a flat dict of current system metrics suitable for heartbeats.

+
+
Parameters:
+

gpu (bool) – When True (default) probe NVIDIA GPUs via nvidia-smi. Set to +False on hot paths to skip the ~200 ms shellout when you know +there’s no GPU or when you cache GPU info separately.

+
+
Returns:
+

See module docstring for the full key list and contract.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+scitex.resource.get_processor_usages()[source]
+

Gets current system resource usage statistics.

+
+
Returns:
+

Resource usage data with columns: +- Timestamp: Timestamp +- CPU [%]: CPU utilization +- RAM [GiB]: RAM usage +- GPU [%]: GPU utilization +- VRAM [GiB]: VRAM usage

+
+
Return type:
+

DataFrame

+
+
+
+

Example

+
>>> df = get_proccessor_usages()
+>>> print(df)
+             Timestamp  CPU [%]  RAM [GiB]  GPU [%]  VRAM [GiB]
+0  2024-11-04 10:30:15    25.3      8.2     65.0        4.5
+
+
+
+
+ +
+
+scitex.resource.get_specs(system=True, cpu=True, gpu=True, disk=True, network=True, verbose=False, yaml=False)[source]
+

Collects and returns system specifications including system information, CPU, GPU, disk, and network details.

+

This function gathers various pieces of system information based on the parameters provided. It can return the data in a dictionary format or print it out based on the verbose flag. Additionally, there’s an option to format the output as YAML.

+
+
Parameters:
+
    +
  • system (bool) – If True, collects system-wide information such as OS and node name. Default is True.

  • +
  • boot_time (bool) – If True, collects system boot time. Currently commented out in the implementation. Default is True.

  • +
  • cpu (bool) – If True, collects CPU-specific information including frequency and usage. Default is True.

  • +
  • gpu (bool) – If True, collects GPU-specific information. Default is True.

  • +
  • disk (bool) – If True, collects disk usage information for all partitions. Default is True.

  • +
  • network (bool) – If True, collects network interface and traffic information. Default is True.

  • +
  • verbose (bool) – If True, prints the collected information using pprint. Default is False.

  • +
  • yaml (bool) – If True, formats the collected information as YAML. This modifies the return type to a YAML formatted string. Default is False.

  • +
+
+
Returns:
+

By default, returns a dictionary containing the collected system specifications. If yaml is True, returns a YAML-formatted string instead.

+
+
Return type:
+

dict or str

+
+
+
+

Note

+
    +
  • The actual collection of system, CPU, GPU, disk, and network information depends on the availability of corresponding libraries and access permissions.

  • +
  • The boot_time argument is currently not used as its corresponding code is commented out.

  • +
  • The function uses global variables and imports within its scope, which might affect its reusability and testability.

  • +
+
+
+

Example

+
>>> specs = get_specs(verbose=True)
+This will print and return the system specifications based on the default parameters.
+
+
+
+
+
Dependencies:
    +
  • This function depends on the scitex library for accessing system information and formatting output. Ensure this library is installed and properly configured.

  • +
  • Python standard libraries: datetime, platform, psutil, yaml (optional for YAML output).

  • +
+
+
+
+
Raises:
+

PermissionError – If the function lacks necessary permissions to access certain system information, especially disk and network details.

+
+
+
+ +
+
+scitex.resource.log_processor_usages(path='/tmp/scitex/processor_usages.csv', limit_min=30, interval_s=1, init=True, verbose=False, background=False)[source]
+

Logs system resource usage over time.

+
+
Parameters:
+
    +
  • path (str) – Path to save the log file

  • +
  • limit_min (float) – Monitoring duration in minutes

  • +
  • interval_s (float) – Sampling interval in seconds

  • +
  • init (bool) – Whether to clear existing log file

  • +
  • verbose (bool) – Whether to print the log

  • +
  • background (bool) – Whether to run in background

  • +
+
+
Returns:
+

Process object if background=True, None otherwise

+
+
Return type:
+

Optional[Process]

+
+
+
+ +
+
+scitex.resource.main(path='/tmp/scitex/processor_usages.csv', limit_min=30, interval_s=1, init=True, verbose=False, background=False)
+

Logs system resource usage over time.

+
+
Parameters:
+
    +
  • path (str) – Path to save the log file

  • +
  • limit_min (float) – Monitoring duration in minutes

  • +
  • interval_s (float) – Sampling interval in seconds

  • +
  • init (bool) – Whether to clear existing log file

  • +
  • verbose (bool) – Whether to print the log

  • +
  • background (bool) – Whether to run in background

  • +
+
+
Returns:
+

Process object if background=True, None otherwise

+
+
Return type:
+

Optional[Process]

+
+
+
+ +
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/rng.html b/src/scitex/_sphinx_html/source/api/rng.html new file mode 100644 index 000000000..7e7797af0 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/rng.html @@ -0,0 +1,1133 @@ + + + + + + + + + rng Module (stx.rng) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

rng Module (stx.rng)

+

scitex-repro — Reproducibility utilities for scientific computing.

+

Provides tools for reproducible scientific computing: +- Random state management (RandomStateManager) +- ID generation (gen_ID) +- Timestamp generation (gen_timestamp) +- Array hashing (hash_array)

+
+
+scitex.rng.gen_ID(time_format='%YY-%mM-%dD-%Hh%Mm%Ss', N=8, *, now_fn=None)
+

Generate a unique identifier with timestamp and random characters.

+

Creates a unique ID by combining a formatted timestamp with random +alphanumeric characters. Useful for creating unique experiment IDs, +run identifiers, or temporary file names.

+
+
Parameters:
+
    +
  • time_format (str, optional) – Format string for timestamp portion. Default is “%YY-%mM-%dD-%Hh%Mm%Ss” +which produces “2025Y-05M-31D-12h30m45s” format.

  • +
  • N (int, optional) – Number of random characters to append. Default is 8.

  • +
  • now_fn (callable, optional) – Zero-argument callable returning a datetime-like object with +.strftime(). Defaults to datetime.now. Injection point for +deterministic tests — pass a fake that returns a fixed datetime +instead of mocking datetime globally.

  • +
+
+
Returns:
+

Unique identifier in format “{timestamp}_{random_chars}”

+
+
Return type:
+

str

+
+
+
+

Examples

+
>>> id1 = gen_id()
+>>> print(id1)
+'2025Y-05M-31D-12h30m45s_a3Bc9xY2'
+
+
+
>>> id2 = gen_id(time_format="%Y%m%d", N=4)
+>>> print(id2)
+'20250531_xY9a'
+
+
+
>>> # For experiment tracking
+>>> exp_id = gen_id()
+>>> save_path = f"results/experiment_{exp_id}.pkl"
+
+
+
+
+ +
+
+scitex.rng.gen_id(time_format='%YY-%mM-%dD-%Hh%Mm%Ss', N=8, *, now_fn=None)[source]
+

Generate a unique identifier with timestamp and random characters.

+

Creates a unique ID by combining a formatted timestamp with random +alphanumeric characters. Useful for creating unique experiment IDs, +run identifiers, or temporary file names.

+
+
Parameters:
+
    +
  • time_format (str, optional) – Format string for timestamp portion. Default is “%YY-%mM-%dD-%Hh%Mm%Ss” +which produces “2025Y-05M-31D-12h30m45s” format.

  • +
  • N (int, optional) – Number of random characters to append. Default is 8.

  • +
  • now_fn (callable, optional) – Zero-argument callable returning a datetime-like object with +.strftime(). Defaults to datetime.now. Injection point for +deterministic tests — pass a fake that returns a fixed datetime +instead of mocking datetime globally.

  • +
+
+
Returns:
+

Unique identifier in format “{timestamp}_{random_chars}”

+
+
Return type:
+

str

+
+
+
+

Examples

+
>>> id1 = gen_id()
+>>> print(id1)
+'2025Y-05M-31D-12h30m45s_a3Bc9xY2'
+
+
+
>>> id2 = gen_id(time_format="%Y%m%d", N=4)
+>>> print(id2)
+'20250531_xY9a'
+
+
+
>>> # For experiment tracking
+>>> exp_id = gen_id()
+>>> save_path = f"results/experiment_{exp_id}.pkl"
+
+
+
+
+ +
+
+scitex.rng.gen_timestamp(*, now_fn=None)[source]
+

Generate a timestamp string for file naming.

+

Returns a timestamp in the format YYYY-MMDD-HHMM, suitable for +creating unique filenames or version identifiers.

+
+
Parameters:
+

now_fn (callable, optional) – Zero-argument callable returning a datetime-like object with +.strftime(). Defaults to datetime.now. Injection point for +deterministic tests — pass a fake that returns a fixed datetime +instead of mocking datetime globally.

+
+
Returns:
+

Timestamp string in format “YYYY-MMDD-HHMM”

+
+
Return type:
+

str

+
+
+
+

Examples

+
>>> timestamp = gen_timestamp()
+>>> print(timestamp)
+'2025-0531-1230'
+
+
+
>>> filename = f"experiment_{gen_timestamp()}.csv"
+>>> print(filename)
+'experiment_2025-0531-1230.csv'
+
+
+
+
+ +
+
+scitex.rng.timestamp(*, now_fn=None)
+

Generate a timestamp string for file naming.

+

Returns a timestamp in the format YYYY-MMDD-HHMM, suitable for +creating unique filenames or version identifiers.

+
+
Parameters:
+

now_fn (callable, optional) – Zero-argument callable returning a datetime-like object with +.strftime(). Defaults to datetime.now. Injection point for +deterministic tests — pass a fake that returns a fixed datetime +instead of mocking datetime globally.

+
+
Returns:
+

Timestamp string in format “YYYY-MMDD-HHMM”

+
+
Return type:
+

str

+
+
+
+

Examples

+
>>> timestamp = gen_timestamp()
+>>> print(timestamp)
+'2025-0531-1230'
+
+
+
>>> filename = f"experiment_{gen_timestamp()}.csv"
+>>> print(filename)
+'experiment_2025-0531-1230.csv'
+
+
+
+
+ +
+
+scitex.rng.hash_array(array_data)[source]
+

Generate hash for array data.

+

Creates a deterministic hash for numpy arrays, useful for +verifying data integrity and reproducibility.

+
+
Parameters:
+

array_data (ndarray) – Array to hash

+
+
Returns:
+

16-character hash string

+
+
Return type:
+

str

+
+
+
+

Examples

+
>>> import numpy as np
+>>> data = np.array([1, 2, 3, 4, 5])
+>>> hash1 = hash_array(data)
+>>> hash2 = hash_array(data)
+>>> hash1 == hash2
+True
+
+
+
+
+ +
+
+class scitex.rng.RandomStateManager(seed=42, verbose=False)[source]
+

Bases: object

+

Simple, robust random state manager for scientific computing.

+
+

Examples

+
>>> from scitex_repro import RandomStateManager
+>>>
+>>> # Method 1: Direct usage
+>>> rng = RandomStateManager(seed=42)
+>>> data = rng("data").random(100)
+>>>
+>>> # Verify reproducibility
+>>> rng.verify(data, "my_data")
+
+
+
+
+
+__init__(seed=42, verbose=False)[source]
+

Initialize with automatic module detection.

+
+ +
+
+get_np_generator(name)[source]
+

Get or create a named NumPy random generator.

+
+
Parameters:
+

name (str) – Generator name (e.g., “data”, “model”, “augment”)

+
+
Returns:
+

Independent NumPy random generator

+
+
Return type:
+

numpy.random.Generator

+
+
+
+

Examples

+
>>> rng = RandomStateManager(42)
+>>> gen = rng.get_np_generator("data")
+>>> values = gen.random(100)
+>>> perm = gen.permutation(100)
+
+
+
+
+ +
+
+__call__(name, verbose=None)[source]
+

Get or create a named NumPy random generator.

+

This is a backward compatibility wrapper for get_np_generator(). +Consider using get_np_generator() directly for clarity.

+
+
Parameters:
+
    +
  • name (str) – Generator name

  • +
  • verbose (bool) – Whether to show deprecation warning

  • +
+
+
Returns:
+

NumPy random generator with deterministic seed

+
+
Return type:
+

numpy.random.Generator

+
+
+
+ +
+
+verify(obj, name=None, verbose=True)[source]
+

Verify object matches cached hash (detects broken reproducibility).

+

First call: caches the object’s hash +Later calls: verifies object matches cached hash

+
+
Parameters:
+
    +
  • obj (Any) – Object to verify (array, tensor, data, model weights, etc.) +Supports: numpy arrays, torch tensors, tf tensors, jax arrays, +lists, dicts, pandas dataframes, and basic types

  • +
  • name (str) – Cache name. Auto-generated if not provided.

  • +
+
+
Returns:
+

True if matches cache (or first call), False if different

+
+
Return type:
+

bool

+
+
+
+

Examples

+
>>> data = generate_data()
+>>> rng.verify(data, "train_data")  # First run: caches
+>>> # Next run:
+>>> rng.verify(data, "train_data")  # Verifies match
+
+
+
+
+ +
+
+checkpoint(name='checkpoint')[source]
+

Save current state of all generators.

+
+ +
+
+restore(checkpoint)[source]
+

Restore from checkpoint.

+
+ +
+
+temporary_seed(seed)[source]
+

Context manager for temporary seed change.

+
+ +
+
+get_sklearn_random_state(name)[source]
+

Get a random state for scikit-learn.

+

Scikit-learn uses integers for random_state parameter.

+
+
Parameters:
+

name (str) – Generator name

+
+
Returns:
+

Random state integer for sklearn

+
+
Return type:
+

int

+
+
+
+

Examples

+
>>> rng = RandomStateManager(42)
+>>> from sklearn.model_selection import train_test_split
+>>> X_train, X_test = train_test_split(
+...     X, test_size=0.2,
+...     random_state=rng.get_sklearn_random_state("split")
+... )
+
+
+
+
+ +
+
+get_torch_generator(name)[source]
+

Get or create a named PyTorch generator.

+
+
Parameters:
+

name (str) – Generator name

+
+
Returns:
+

PyTorch generator with deterministic seed

+
+
Return type:
+

torch.Generator

+
+
+
+

Examples

+
>>> rng = RandomStateManager(42)
+>>> gen = rng.get_torch_generator("model")
+>>> torch.randn(5, 5, generator=gen)
+
+
+
+
+ +
+
+get_generator(name)[source]
+

Alias for get_np_generator for compatibility.

+
+ +
+
+clear_cache(patterns=None)[source]
+

Clear verification cache files.

+
+
Parameters:
+

patterns (str | list[str]) – Specific cache patterns to clear. If None, clears all.

+
+
Returns:
+

Number of cache files removed

+
+
Return type:
+

int

+
+
+
+ +
+ +
+
+scitex.rng.get(verbose=False)[source]
+

Get or create the global RandomStateManager instance.

+
+
Parameters:
+

verbose (bool) – Whether to print status messages (default: False)

+
+
Returns:
+

Global instance

+
+
Return type:
+

RandomStateManager

+
+
+
+

Examples

+
>>> from scitex_repro import get
+>>> rng = get()
+>>> data = rng("data").random(100)
+
+
+
+
+ +
+
+scitex.rng.reset(seed=42, verbose=False)[source]
+

Reset global RandomStateManager with new seed.

+
+
Parameters:
+
    +
  • seed (int) – New seed value

  • +
  • verbose (bool) – Whether to print status messages (default: False)

  • +
+
+
Returns:
+

New global instance

+
+
Return type:
+

RandomStateManager

+
+
+
+

Examples

+
>>> from scitex_repro import reset
+>>> rng = reset(seed=123)
+
+
+
+
+ +
+
+scitex.rng.fix_seeds(seed=42, os=True, random=True, np=True, torch=True, tf=False, jax=False, verbose=False, **kwargs)[source]
+

Deprecated: Use RandomStateManager instead.

+

This function maintains backward compatibility with the old fix_seeds API.

+
+ +
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/schema.html b/src/scitex/_sphinx_html/source/api/schema.html new file mode 100644 index 000000000..0ecbd7195 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/schema.html @@ -0,0 +1,674 @@ + + + + + + + + + schema Module (stx.schema) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

schema Module (stx.schema)

+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/scholar.html b/src/scitex/_sphinx_html/source/api/scholar.html new file mode 100644 index 000000000..7d9758d63 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/scholar.html @@ -0,0 +1,794 @@ + + + + + + + + + Scholar Module (stx.scholar) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Scholar Module (stx.scholar)

+

Literature management: search papers, download PDFs, enrich BibTeX, +and organize a local library across multiple projects.

+
+

Quick Reference

+
from scitex.scholar import Scholar
+
+scholar = Scholar(project="my_research")
+
+# Load and enrich BibTeX
+papers = scholar.load_bibtex("references.bib")
+enriched = scholar.enrich_papers(papers)
+# Adds: DOIs, abstracts, citation counts, impact factors
+
+# Save to library and export
+scholar.save_papers_to_library(enriched)
+scholar.save_papers_as_bibtex(enriched, "enriched.bib")
+
+# Search your library
+results = scholar.search_library("neural oscillations")
+
+# Download PDFs
+scholar.download_pdfs(dois, output_dir)
+
+
+
+
+

CLI Usage

+
# Full pipeline from BibTeX
+scitex scholar bibtex refs.bib --project myresearch --num-workers 8
+
+# Search papers
+scitex scholar search "deep learning EEG"
+
+# Download PDFs
+scitex scholar download --doi 10.1038/nature12373
+
+# Institutional authentication
+scitex scholar auth --method openathens
+scitex scholar auth --method shibboleth --institution "MIT"
+
+
+
+
+

Data Sources

+

Searches and enriches from:

+
    +
  • CrossRef (167M+ papers) – DOI resolution, citation counts

  • +
  • Semantic Scholar – Abstracts, references, influence scores

  • +
  • PubMed – Biomedical literature

  • +
  • arXiv – Preprints

  • +
  • OpenAlex (284M+ works) – Open metadata

  • +
+
+
+

Key Classes

+
    +
  • Scholar – Main entry point (search, enrich, download, organize)

  • +
  • Paper – Type-safe metadata container (Pydantic model)

  • +
  • Papers – Collection with filtering, sorting, and export

  • +
  • ScholarConfig – YAML-based configuration

  • +
  • ScholarLibrary – Local library storage and caching

  • +
+
+
+

Paper Metadata

+

Each Paper contains structured metadata sections:

+
paper.metadata.basic          # title, authors, year, abstract, keywords
+paper.metadata.id             # DOI, arXiv, PMID, Semantic Scholar ID
+paper.metadata.publication    # journal, impact factor, volume, issue
+paper.metadata.citation_count # total + yearly breakdown (2015--2024)
+paper.metadata.url            # DOI URL, publisher, arXiv, PDFs
+paper.metadata.access         # open access status, license
+
+
+
+
+

Filtering and Sorting

+
# Criteria-based filtering
+recent = papers.filter(year_min=2020, has_doi=True)
+elite = papers.filter(min_impact_factor=10, min_citations=500)
+
+# Lambda filtering
+custom = papers.filter(lambda p: "EEG" in (p.metadata.basic.title or ""))
+
+# Sorting
+papers.sort_by("year", reverse=True)
+papers.sort_by("citation_count", reverse=True)
+
+# Chaining
+top_recent = papers.filter(year_min=2020).sort_by("citation_count", reverse=True)
+
+
+
+
+

Project Organization

+
scholar = Scholar(project="review_paper")
+scholar.list_projects()
+papers = scholar.load_project()
+
+# Export to multiple formats
+scholar.save_papers_as_bibtex(papers, "output.bib")
+papers.to_dataframe()  # pandas DataFrame
+
+
+
+
+

Storage Architecture

+
~/.scitex/scholar/library/
++-- MASTER/                     # Centralized master storage
+|   +-- 8DIGIT01/              # Hash-based unique ID from DOI
+|   |   +-- metadata.json
+|   |   +-- paper.pdf
++-- project_name/               # Project-specific symlinks
+    +-- Author-Year-Journal -> ../MASTER/8DIGIT01
+
+
+
+
+

API Reference

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/security.html b/src/scitex/_sphinx_html/source/api/security.html new file mode 100644 index 000000000..c17153762 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/security.html @@ -0,0 +1,774 @@ + + + + + + + + + security Module (stx.security) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

security Module (stx.security)

+

scitex-security — GitHub security-alert utilities (standalone).

+
+
Usage:

from scitex_security import check_github_alerts

+

alerts = check_github_alerts() +if alerts:

+
+

print(f”Found {len(alerts)} security alerts!”)

+
+
+
+
+
+scitex.security.check_github_alerts(repo=None, *, auth_check=None, secrets_fn=None, dependabot_fn=None, code_scanning_fn=None)[source]
+

Check all GitHub security alerts.

+
+
Parameters:
+
    +
  • repo (Optional[str]) – Repository in format ‘owner/repo’. If None, uses current repo.

  • +
  • auth_check (Optional[Callable[[], bool]]) – check_gh_auth-shaped callable. Override in tests.

  • +
  • secrets_fn (Optional[Callable]) – get_secret_alerts-shaped callable. Override in tests.

  • +
  • dependabot_fn (Optional[Callable]) – get_dependabot_alerts-shaped callable. Override in +tests.

  • +
  • code_scanning_fn (Optional[Callable]) – get_code_scanning_alerts-shaped callable. +Override in tests.

  • +
+
+
Returns:
+

‘secrets’, ‘dependabot’, ‘code_scanning’

+
+
Return type:
+

Dict[str, List[Dict]]

+
+
Raises:
+

GitHubSecurityError – If GitHub CLI is not installed or not authenticated

+
+
+
+ +
+
+scitex.security.save_alerts_to_file(alerts, output_dir=None, create_symlink=True)[source]
+

Save alerts to a timestamped file.

+
+
Parameters:
+
    +
  • alerts (Dict[str, List[Dict]]) – Dictionary of alerts from check_github_alerts()

  • +
  • output_dir (Optional[Path]) – Directory to save file. Defaults to ./logs/security

  • +
  • create_symlink (bool) – If True, create ‘security-latest.txt’ symlink

  • +
+
+
Return type:
+

Path

+
+
Returns:
+

Path to saved file

+
+
+
+ +
+
+scitex.security.get_latest_alerts_file(security_dir=None)[source]
+

Get path to the latest security alerts file.

+
+
Parameters:
+

security_dir (Optional[Path]) – Directory containing security files. Defaults to ./logs/security

+
+
Return type:
+

Optional[Path]

+
+
Returns:
+

Path to latest file, or None if not found

+
+
+
+ +
+
+scitex.security.format_alerts_report(alerts)[source]
+

Format alerts into a readable text report.

+
+
Parameters:
+

alerts (Dict[str, List[Dict]]) – Dictionary of alerts from check_github_alerts()

+
+
Return type:
+

str

+
+
Returns:
+

Formatted text report

+
+
+
+ +
+
+exception scitex.security.GitHubSecurityError[source]
+

Bases: Exception

+

Raised when GitHub security operations fail.

+
+ +
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/session.html b/src/scitex/_sphinx_html/source/api/session.html new file mode 100644 index 000000000..c1e9c3d43 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/session.html @@ -0,0 +1,883 @@ + + + + + + + + + Session Decorator (@stx.session) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Session Decorator (@stx.session)

+

The @stx.session decorator is the primary entry point for SciTeX +scripts. It wraps a function to provide automatic CLI generation, +output directory management, configuration injection, and provenance tracking.

+
+

Basic Usage

+
import scitex as stx
+
+@stx.session
+def main(
+    data_path="data.csv",       # CLI: --data-path data.csv
+    threshold=0.5,              # CLI: --threshold 0.7
+    CONFIG=stx.INJECTED,        # Auto-injected session config
+    plt=stx.INJECTED,           # Pre-configured matplotlib
+    logger=stx.INJECTED,        # Session logger
+):
+    """Analyze experimental data."""
+    data = stx.io.load(data_path)
+    result = process(data, threshold)
+    stx.io.save(result, "output.csv")
+    return 0  # exit code (0 = success)
+
+if __name__ == "__main__":
+    main()  # No arguments triggers CLI mode
+
+
+

Run from the command line:

+
python my_script.py --data-path experiment.csv --threshold 0.3
+python my_script.py --help  # Shows all parameters with defaults
+
+
+
+
+

How It Works

+Session lifecycle: main() → Parse CLI → Create dir → Load config → Execute → SUCCESS or ERROR + +

When main() is called without arguments, the decorator:

+
    +
  1. Parses CLI arguments from the function signature (type hints and defaults become argparse options)

  2. +
  3. Creates a session directory under script_out/RUNNING/{session_id}/

  4. +
  5. Loads configuration from ./config/*.yaml files into CONFIG

  6. +
  7. Injects globals (CONFIG, plt, COLORS, logger, rngg) into the function

  8. +
  9. Redirects stdout/stderr to log files

  10. +
  11. Executes the function with parsed arguments

  12. +
  13. Moves output from RUNNING/ to FINISHED_SUCCESS/ (or FINISHED_ERROR/)

  14. +
+

When called with arguments (e.g., main(data_path="x.csv")), the decorator +is bypassed and the function runs directly – useful for testing and notebooks.

+
+
+

Output Directory Structure

+

Each run produces a self-contained output directory:

+
my_script_out/
++-- FINISHED_SUCCESS/
+|   +-- 2026Y-02M-13D-14h30m15s_Z5MR/
+|       +-- CONFIGS/
+|       |   +-- CONFIG.yaml     # Frozen configuration
+|       |   +-- CONFIG.pkl      # Pickled config
+|       +-- logs/
+|       |   +-- stdout.log      # Captured stdout
+|       |   +-- stderr.log      # Captured stderr
+|       +-- output.csv          # Your saved files
+|       +-- sine.png            # Your saved figures
+|       +-- sine.csv            # Auto-exported figure data
++-- FINISHED_ERROR/
+|   +-- ...                     # Runs that returned non-zero
++-- RUNNING/
+    +-- ...                     # Currently active sessions
+
+
+

The session ID format is YYYY-MM-DD-HH:MM:SS_XXXX where XXXX is +a random 4-character suffix for uniqueness.

+
+
+

Injected Parameters

+

Use stx.INJECTED as the default value to receive auto-injected objects:

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Parameter Name

Type

Description

CONFIG

DotDict

Session config with ID, SDIR_RUN, FILE, ARGS, plus all ./config/*.yaml values

plt

module

matplotlib.pyplot configured for the session (Agg backend, style settings)

COLORS

DotDict

Color palette for consistent plotting

logger

Logger

SciTeX logger writing to session log files

rngg

RandomStateManager

Reproducibility manager (seeds fixed by default)

+

Only request the parameters you need:

+
@stx.session
+def main(n=100, CONFIG=stx.INJECTED):
+    """Minimal example -- only CONFIG injected."""
+    print(f"Session ID: {CONFIG.ID}")
+    print(f"Output dir: {CONFIG.SDIR_RUN}")
+    return 0
+
+
+
+
+

CONFIG Object

+

The injected CONFIG is a DotDict supporting both dictionary and +dot-notation access:

+
CONFIG["MODEL"]["hidden_size"]   # dict-style
+CONFIG.MODEL.hidden_size         # dot-style (equivalent)
+
+
+

It contains:

+
CONFIG.ID             # "2026Y-02M-13D-14h30m15s_Z5MR"
+CONFIG.PID            # Process ID
+CONFIG.FILE           # Path to the script
+CONFIG.SDIR_OUT       # Base output directory
+CONFIG.SDIR_RUN       # Current session's output directory
+CONFIG.START_DATETIME # When the session started
+CONFIG.ARGS           # Parsed CLI arguments as dict
+CONFIG.EXIT_STATUS    # 0 (success), 1 (error), or None
+
+
+

Any YAML files in ./config/ are merged into CONFIG:

+
# ./config/EXPERIMENT.yaml
+learning_rate: 0.001
+batch_size: 32
+
+
+
# Accessible as:
+CONFIG.EXPERIMENT.learning_rate  # 0.001
+CONFIG.EXPERIMENT.batch_size     # 32
+
+
+
+
+

Decorator Options

+
@stx.session(verbose=True, agg=False, notify=True, sdir_suffix="v2")
+def main(...):
+    ...
+
+
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Option

Type

Default

Description

verbose

bool

False

Enable verbose logging

agg

bool

True

Use matplotlib Agg backend (set False for interactive plots)

notify

bool

False

Send notification when session completes

sdir_suffix

str

None

Append suffix to output directory name

+
+
+

Best Practices

+
    +
  1. One session per script – each .py file should have one @stx.session function

  2. +
  3. Return 0 for success – the return value becomes the exit status

  4. +
  5. Save outputs with stx.io.save – files go into the session directory and are provenance-tracked

  6. +
  7. Put config in YAML – use ./config/*.yaml instead of hardcoding parameters

  8. +
  9. Use stx.repro.fix_seeds() – already called by default via rngg

  10. +
+
+
+

API Reference

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/sh.html b/src/scitex/_sphinx_html/source/api/sh.html new file mode 100644 index 000000000..d0ac3e6c8 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/sh.html @@ -0,0 +1,674 @@ + + + + + + + + + sh Module (stx.sh) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

sh Module (stx.sh)

+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/social.html b/src/scitex/_sphinx_html/source/api/social.html new file mode 100644 index 000000000..07e1e1d64 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/social.html @@ -0,0 +1,674 @@ + + + + + + + + + social Module (stx.social) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

social Module (stx.social)

+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/stats.html b/src/scitex/_sphinx_html/source/api/stats.html new file mode 100644 index 000000000..920360e58 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/stats.html @@ -0,0 +1,813 @@ + + + + + + + + + Stats Module (stx.stats) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Stats Module (stx.stats)

+

23 statistical tests with effect sizes, confidence intervals, and +publication-ready formatting.

+
+

Quick Reference

+
import scitex as stx
+import numpy as np
+
+g1 = np.random.normal(0, 1, 50)
+g2 = np.random.normal(0.5, 1, 50)
+
+# Run a test
+result = stx.stats.test_ttest_ind(g1, g2)
+
+# Result is a flat dict with all details
+print(result["statistic"])     # t-statistic
+print(result["pvalue"])        # p-value
+print(result["effect_size"])   # Cohen's d
+print(result["stars"])         # Significance stars
+
+# Get as DataFrame or LaTeX
+df = stx.stats.test_ttest_ind(g1, g2, return_as="dataframe")
+tex = stx.stats.test_ttest_ind(g1, g2, return_as="latex")
+
+
+
+
+

Available Tests

+

Parametric

+
    +
  • test_ttest_1samp(data, popmean) – One-sample t-test

  • +
  • test_ttest_ind(g1, g2) – Independent two-sample t-test

  • +
  • test_ttest_rel(g1, g2) – Paired t-test

  • +
  • test_anova(*groups) – One-way ANOVA

  • +
  • test_anova_2way(data, factor1, factor2) – Two-way ANOVA

  • +
  • test_anova_rm(data, groups) – Repeated measures ANOVA

  • +
+

Non-parametric

+
    +
  • test_mannwhitneyu(g1, g2) – Mann-Whitney U test

  • +
  • test_wilcoxon(g1, g2) – Wilcoxon signed-rank test

  • +
  • test_kruskal(*groups) – Kruskal-Wallis H test

  • +
  • test_friedman(*groups) – Friedman test

  • +
  • test_brunner_munzel(g1, g2) – Brunner-Munzel test

  • +
+

Correlation

+
    +
  • test_pearson(x, y) – Pearson correlation

  • +
  • test_spearman(x, y) – Spearman rank correlation

  • +
  • test_kendall(x, y) – Kendall tau correlation

  • +
  • test_theilsen(x, y) – Theil-Sen robust regression

  • +
+

Categorical

+
    +
  • test_chi2(observed) – Chi-squared test

  • +
  • test_fisher(table) – Fisher’s exact test

  • +
  • test_mcnemar(table) – McNemar test

  • +
  • test_cochran_q(*groups) – Cochran’s Q test

  • +
+

Normality

+
    +
  • test_shapiro(data) – Shapiro-Wilk test

  • +
  • test_ks_1samp(data) – One-sample Kolmogorov-Smirnov test

  • +
  • test_ks_2samp(x, y) – Two-sample Kolmogorov-Smirnov test

  • +
  • test_normality(*samples) – Multi-sample normality check

  • +
+
+
+

Seaborn-Style Data Parameter

+

All two-sample and one-sample tests accept an optional data parameter +for DataFrame/CSV column resolution (like seaborn):

+
import pandas as pd
+
+df = pd.read_csv("experiment.csv")
+
+# Two-sample: column names as x/y
+result = stx.stats.test_ttest_ind(x="before", y="after", data=df)
+
+# One-sample: column name as x
+result = stx.stats.test_shapiro(x="scores", data=df)
+
+# Multi-group: value + group columns
+result = stx.stats.test_anova(data=df, value_col="score", group_col="treatment")
+
+# Also works with CSV path
+result = stx.stats.test_ttest_ind(x="col1", y="col2", data="data.csv")
+
+
+
+
+

Test Recommendation

+

Not sure which test to use? Let SciTeX recommend:

+
recommendations = stx.stats.recommend_tests(
+    n_groups=2,
+    sample_sizes=[30, 35],
+    outcome_type="continuous",
+    paired=False,
+)
+
+
+
+
+

Output Formats

+

Every test supports return_as parameter:

+
    +
  • "dict" (default) – Returns plain dict with all results

  • +
  • "dataframe" – Returns pandas DataFrame

  • +
+
+
+

Descriptive Statistics

+
stx.stats.describe(data)                    # mean, std, median, IQR, etc.
+stx.stats.effect_sizes.cohens_d(g1, g2)     # Cohen's d
+stx.stats.test_normality(g1, g2)            # Multi-sample normality
+stx.stats.p_to_stars(0.003)                 # "**"
+
+
+
+
+

Multiple Comparison Correction

+
corrected = stx.stats.correct_pvalues(
+    [0.01, 0.03, 0.05, 0.001],
+    method="fdr_bh",
+)
+
+
+
+
+

Post-hoc Tests

+
stx.stats.posthoc_test(
+    [g1, g2, g3],
+    group_names=["Control", "Treatment A", "Treatment B"],
+    method="tukey",
+)
+
+
+
+
+

API Reference

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/str.html b/src/scitex/_sphinx_html/source/api/str.html new file mode 100644 index 000000000..e6369fb8d --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/str.html @@ -0,0 +1,674 @@ + + + + + + + + + str Module (stx.str) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

str Module (stx.str)

+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/template.html b/src/scitex/_sphinx_html/source/api/template.html new file mode 100644 index 000000000..8b806971d --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/template.html @@ -0,0 +1,824 @@ + + + + + + + + + Template Module (stx.template) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Template Module (stx.template)

+

Project scaffolding and code snippet templates for quick starts. +Create fully structured research projects, Python packages, or +academic papers with a single command.

+
+

Project Templates

+
# Clone a project template
+scitex template clone research my_project
+scitex template clone pip_project my_package
+scitex template clone paper my_paper
+scitex template clone app my_app
+scitex template clone singularity my_container
+
+
+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + +

Template

Description

research

Full scientific workflow (scripts, data, docs, config, session scaffolding)

research_minimal

Essential modules only for lightweight experiments

pip_project

Distributable Python package for PyPI (pyproject.toml, tests, CI)

paper

Academic paper with LaTeX/BibTeX structure, SciTeX writer integration

app

SciTeX application with bridge-init, MountPoint, and EventBus

singularity

Reproducible containerized environment (Singularity/Apptainer definition)

+
+

What You Get

+

A research template creates:

+
my_project/
++-- config/
+|   +-- EXPERIMENT.yaml       # Experiment parameters
++-- scripts/
+|   +-- main.py               # @stx.session entry point
++-- data/                     # Raw data directory
++-- docs/                     # Documentation
++-- tests/                    # Test suite
++-- Makefile                  # Common tasks
++-- pyproject.toml            # Package metadata
+
+
+
+
+
+

Git Strategies

+

Control how git is initialized in the cloned template:

+ ++++ + + + + + + + + + + + + + + + + + + + +

Strategy

Behavior

child (default)

Fresh git repo, isolated from template

parent

Use parent repo if available

origin

Preserve template’s git history

None

No git initialization

+
from scitex.template import clone_template
+
+clone_template("research", "my_project", git_strategy="child")
+
+
+
+
+

Code Templates

+

Ready-to-use code snippets for common SciTeX patterns. Each snippet +is a complete, runnable script.

+
# List available code templates
+scitex template code list
+
+# Get a template (prints to stdout)
+scitex template code session           # Full @stx.session script
+scitex template code session-minimal   # Lightweight session
+scitex template code session-plot      # Figure-focused session
+scitex template code session-stats     # Statistics-focused session
+scitex template code io                # I/O operations
+scitex template code plt               # Plotting patterns
+scitex template code stats             # Statistical analysis
+scitex template code scholar           # Literature management
+
+# Save to a file
+scitex template code session --output analyze.py
+
+
+
+
+

Python API

+
from scitex.template import clone_template, get_code_template
+
+# Clone a project template
+clone_template("research", "my_experiment", git_strategy="child")
+
+# Get a code snippet as a string
+code = get_code_template("session")
+print(code)
+
+# Get a code snippet and write to file
+code = get_code_template("session", filepath="analyze.py")
+
+
+
+
+

MCP Interface

+

Code templates are also available to AI agents via MCP tools:

+
# List templates
+template_list_code_templates()
+
+# Get a specific template
+template_get_code_template(name="session-plot")
+
+# Clone a project
+template_clone_template(template_type="research", name="my_project")
+
+
+
+
+

API Reference

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/tex.html b/src/scitex/_sphinx_html/source/api/tex.html new file mode 100644 index 000000000..1fd153aea --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/tex.html @@ -0,0 +1,674 @@ + + + + + + + + + tex Module (stx.tex) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

tex Module (stx.tex)

+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/torch.html b/src/scitex/_sphinx_html/source/api/torch.html new file mode 100644 index 000000000..a25b24bf3 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/torch.html @@ -0,0 +1,674 @@ + + + + + + + + + torch Module (stx.torch) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

torch Module (stx.torch)

+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/tunnel.html b/src/scitex/_sphinx_html/source/api/tunnel.html new file mode 100644 index 000000000..c8756fa0c --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/tunnel.html @@ -0,0 +1,674 @@ + + + + + + + + + tunnel Module (stx.tunnel) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

tunnel Module (stx.tunnel)

+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/types.html b/src/scitex/_sphinx_html/source/api/types.html new file mode 100644 index 000000000..4cae70141 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/types.html @@ -0,0 +1,674 @@ + + + + + + + + + types Module (stx.types) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

types Module (stx.types)

+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/ui.html b/src/scitex/_sphinx_html/source/api/ui.html new file mode 100644 index 000000000..09f2f4e97 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/ui.html @@ -0,0 +1,734 @@ + + + + + + + + + ui Module (stx.ui) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

ui Module (stx.ui)

+

scitex-ui — Shared frontend UI components for the SciTeX ecosystem.

+

Components ship as TypeScript + CSS static assets, discoverable by Django’s +AppDirectoriesFinder when added to INSTALLED_APPS.

+

Python API provides component metadata and registration.

+
+
+scitex.ui.get_component(name)[source]
+

Get metadata for a registered component.

+
+
Return type:
+

Optional[Any]

+
+
+
+ +
+
+scitex.ui.list_components()[source]
+

List all registered component names.

+
+
Return type:
+

list[str]

+
+
+
+ +
+
+scitex.ui.get_static_dir()[source]
+

Return the absolute path to scitex_ui’s static asset directory.

+

This is the directory containing css/ and ts/ subdirectories. +Works for both pip-installed packages and editable (dev) installs.

+

Useful for build tools (Vite, Webpack) that need to resolve +scitex-ui source files at build time.

+
+
Returns:
+

e.g. /usr/lib/python3.11/.../scitex_ui/static/scitex_ui

+
+
Return type:
+

Path

+
+
+
+ +
+
+scitex.ui.get_docs_path()[source]
+

Return the absolute path to scitex_ui’s bundled documentation directory.

+

The directory contains APP_DEVELOPER_GUIDE.md and the Sphinx-built +HTML docs. Works for both pip-installed packages and editable (dev) installs.

+
+
Returns:
+

e.g. /usr/lib/python3.11/.../scitex_ui/_docs

+
+
Return type:
+

Path

+
+
+
+ +
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/utils.html b/src/scitex/_sphinx_html/source/api/utils.html new file mode 100644 index 000000000..4fed35e52 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/utils.html @@ -0,0 +1,674 @@ + + + + + + + + + utils Module (stx.utils) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

utils Module (stx.utils)

+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/web.html b/src/scitex/_sphinx_html/source/api/web.html new file mode 100644 index 000000000..86760ab51 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/web.html @@ -0,0 +1,674 @@ + + + + + + + + + web Module (stx.web) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

web Module (stx.web)

+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/api/writer.html b/src/scitex/_sphinx_html/source/api/writer.html new file mode 100644 index 000000000..7bae936b3 --- /dev/null +++ b/src/scitex/_sphinx_html/source/api/writer.html @@ -0,0 +1,1274 @@ + + + + + + + + + Writer Module (stx.writer) — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Writer Module (stx.writer)

+

LaTeX manuscript compilation, figure/table management, bibliography +handling, and IMRAD writing guidelines.

+
+

Note

+

stx.writer wraps the standalone +scitex-writer package. +Install with: pip install scitex-writer.

+
+
+

Quick Reference

+
from scitex.writer import Writer
+from pathlib import Path
+
+writer = Writer(Path("my_paper"))
+
+# Compile manuscript → PDF
+result = writer.compile_manuscript()
+if result.success:
+    print(f"PDF: {result.output_pdf}")
+
+# Compile supplementary materials
+result = writer.compile_supplementary()
+
+# Compile revision with change tracking
+result = writer.compile_revision(track_changes=True)
+
+
+
+
+

CLI Usage

+
# Compile
+scitex writer compile manuscript ./my-paper
+scitex writer compile supplementary ./my-paper
+scitex writer compile revision ./my-paper --track-changes
+
+# Bibliography
+scitex writer bib list ./my-paper
+scitex writer bib add ./my-paper "@article{...}"
+
+# Tables and figures
+scitex writer tables add ./my-paper data.csv
+scitex writer figures list ./my-paper
+
+# Writing guidelines
+scitex writer guidelines get abstract
+
+
+
+
+

Project Structure

+

A writer project follows this layout:

+
my_paper/
++-- 00_shared/
+|   +-- bibliography.bib
+|   +-- figures/
+|   +-- tables/
++-- 01_manuscript/
+|   +-- main.tex
+|   +-- sections/
++-- 02_supplementary/
+|   +-- main.tex
++-- 03_revision/
+    +-- main.tex
+
+
+

Create a new project:

+
scitex template clone paper my_paper
+
+
+
+
+

Key Classes

+
    +
  • Writer – Main entry point for compilation and project management

  • +
  • CompilationResult – Compilation outcome (success, output path, logs)

  • +
  • ManuscriptTree – Document tree for the main manuscript

  • +
  • SupplementaryTree – Document tree for supplementary materials

  • +
  • RevisionTree – Document tree for revision responses

  • +
+
+
+

Document Management

+

Figures:

+
# Add figure (copies image + creates caption file)
+writer.add_figure("fig1", "plot.png", caption="Results")
+
+# List figures
+writer.list_figures()
+
+# Convert between formats
+writer.convert_figure("figure.pdf", "figure.png", dpi=300)
+
+
+

Tables:

+
# Add table from CSV
+writer.add_table("tab1", csv_content, caption="Demographics")
+
+# CSV ↔ LaTeX conversion
+writer.csv_to_latex("data.csv", "table.tex")
+writer.latex_to_csv("table.tex", "data.csv")
+
+
+

Bibliography:

+
# Add BibTeX entry
+writer.add_bibentry('@article{key, title={...}, ...}')
+
+# Merge multiple .bib files
+writer.merge_bibfiles(output_file="bibliography.bib")
+
+
+
+
+

Writing Guidelines

+

IMRAD guidelines for each manuscript section:

+
scitex writer guidelines list
+scitex writer guidelines get introduction
+
+
+
from scitex.writer import guidelines
+
+# Get guideline for a section
+guide = guidelines.get("methods")
+
+# Build editing prompt: guideline + draft
+prompt = guidelines.build("discussion", draft_text)
+
+
+
+
+

Compilation Options

+
result = writer.compile_manuscript(
+    timeout=300,        # Max compilation time (seconds)
+    no_figs=False,      # Exclude figures
+    no_tables=False,    # Exclude tables
+    draft=False,        # Draft mode (fast, lower quality)
+    dark_mode=False,    # Dark color scheme
+    quiet=False,        # Suppress output
+)
+
+
+
+
+

API Reference

+

SciTeX Writer - LaTeX manuscript compilation system with MCP server.

+
+
Three Interfaces:
    +
  • Python API: import scitex_writer as sw

  • +
  • CLI: scitex-writer <command>

  • +
  • MCP: 30 tools for AI agents

  • +
+
+
Modules:
    +
  • compile: Compile manuscripts to PDF

  • +
  • export: Export manuscript for arXiv submission

  • +
  • project: Clone, info, get_pdf

  • +
  • tables: List, add, remove, csv_to_latex

  • +
  • figures: List, add, remove, convert

  • +
  • bib: List, add, remove, merge

  • +
  • guidelines: IMRAD writing tips

  • +
  • prompts: AI2 Asta integration

  • +
+
+
+
+
+scitex.writer.usage()
+

Get the usage guide with branding applied.

+
+
Returns:
+

Formatted usage guide string.

+
+
Return type:
+

str

+
+
+
+ +
+
+class scitex.writer.Writer(project_dir, name=None, git_strategy='child', branch=None, tag=None)[source]
+

Bases: object

+

LaTeX manuscript compiler.

+
+
+__init__(project_dir, name=None, git_strategy='child', branch=None, tag=None)[source]
+

Initialize for project directory.

+

If directory doesn’t exist, creates new project.

+
+
Parameters:
+
    +
  • project_dir (Path) – Path to project directory.

  • +
  • name (Optional[str]) – Project name (used if creating new project).

  • +
  • git_strategy (Optional[str]) – Git initialization strategy: +- ‘child’: Create isolated git in project directory (default) +- ‘parent’: Use parent git repository +- ‘origin’: Preserve template’s original git history +- None or ‘none’: Disable git initialization

  • +
  • branch (Optional[str]) – Specific branch of template repository to clone. +If None, clones the default branch. Mutually exclusive with tag.

  • +
  • tag (Optional[str]) – Specific tag/release of template repository to clone. +If None, clones the default branch. Mutually exclusive with branch.

  • +
+
+
+
+ +
+
+compile_manuscript(timeout=300, log_callback=None, progress_callback=None)[source]
+

Compile manuscript to PDF with optional live callbacks.

+

Runs scripts/shell/compile_manuscript.sh with configured settings.

+
+
Parameters:
+
    +
  • timeout (int) – Maximum compilation time in seconds (default: 300).

  • +
  • log_callback (Optional[Callable[[str], None]]) – Called with each log line: log_callback(“Running pdflatex…”).

  • +
  • progress_callback (Optional[Callable[[int, str], None]]) – Called with progress: progress_callback(50, “Pass 2/3”).

  • +
+
+
Returns:
+

With success status, PDF path, and errors/warnings.

+
+
Return type:
+

CompilationResult

+
+
+
+

Examples

+
>>> writer = Writer(Path("my_paper"))
+>>> result = writer.compile_manuscript()
+>>> if result.success:
+...     print(f"PDF created: {result.output_pdf}")
+
+
+
+
+ +
+
+compile_supplementary(timeout=300, log_callback=None, progress_callback=None)[source]
+

Compile supplementary materials to PDF with optional live callbacks.

+

Runs scripts/shell/compile_supplementary.sh with configured settings.

+
+
Parameters:
+
+
+
Returns:
+

With success status, PDF path, and errors/warnings.

+
+
Return type:
+

CompilationResult

+
+
+
+

Examples

+
>>> writer = Writer(Path("my_paper"))
+>>> result = writer.compile_supplementary()
+>>> if result.success:
+...     print(f"PDF created: {result.output_pdf}")
+
+
+
+
+ +
+
+compile_revision(track_changes=False, timeout=300, log_callback=None, progress_callback=None)[source]
+

Compile revision document with optional change tracking and live callbacks.

+

Runs scripts/shell/compile_revision.sh with configured settings.

+
+
Parameters:
+
    +
  • track_changes (bool) – Enable change tracking in compiled PDF (default: False).

  • +
  • timeout (int) – Maximum compilation time in seconds (default: 300).

  • +
  • log_callback (Optional[Callable[[str], None]]) – Called with each log line.

  • +
  • progress_callback (Optional[Callable[[int, str], None]]) – Called with progress updates.

  • +
+
+
Returns:
+

With success status, PDF path, and errors/warnings.

+
+
Return type:
+

CompilationResult

+
+
+
+

Examples

+
>>> writer = Writer(Path("my_paper"))
+>>> result = writer.compile_revision(track_changes=True)
+>>> if result.success:
+...     print(f"Revision PDF: {result.output_pdf}")
+
+
+
+
+ +
+
+watch(on_compile=None)[source]
+

Auto-recompile on file changes.

+
+
Return type:
+

None

+
+
+
+ +
+
+get_pdf(doc_type='manuscript')[source]
+

Get output PDF path (Read).

+
+
Return type:
+

Optional[Path]

+
+
+
+ +
+
+delete()[source]
+

Delete project directory (Delete).

+
+
Return type:
+

bool

+
+
+
+ +
+ +
+
+class scitex.writer.CompilationResult(success, exit_code, stdout, stderr, output_pdf=None, diff_pdf=None, log_file=None, duration=0.0, errors=<factory>, warnings=<factory>)[source]
+

Bases: object

+

Result of LaTeX compilation.

+
+
+success: bool
+

Whether compilation succeeded (exit code 0)

+
+ +
+
+exit_code: int
+

Process exit code

+
+ +
+
+stdout: str
+

Standard output from compilation

+
+ +
+
+stderr: str
+

Standard error from compilation

+
+ +
+
+output_pdf: Path | None = None
+

Path to generated PDF (if successful)

+
+ +
+
+diff_pdf: Path | None = None
+

Path to diff PDF with tracked changes (if generated)

+
+ +
+
+log_file: Path | None = None
+

Path to compilation log file

+
+ +
+
+duration: float = 0.0
+

Compilation duration in seconds

+
+ +
+
+errors: List[str]
+

Parsed LaTeX errors (if any)

+
+ +
+
+warnings: List[str]
+

Parsed LaTeX warnings (if any)

+
+ +
+
+__str__()[source]
+

Human-readable summary.

+
+ +
+ +
+
+class scitex.writer.ManuscriptTree(root, git_root=None, contents=None, base=None, readme=None, archive=None)[source]
+

Bases: object

+

Manuscript directory structure (01_manuscript/).

+
+
+root: Path
+
+ +
+
+git_root: Path | None = None
+
+ +
+
+contents: ManuscriptContents = None
+
+ +
+
+base: DocumentSection = None
+
+ +
+
+readme: DocumentSection = None
+
+ +
+
+archive: Path = None
+
+ +
+
+__post_init__()[source]
+

Initialize all instances.

+
+ +
+
+verify_structure()[source]
+

Verify manuscript structure has required components.

+
+
Return type:
+

tuple[bool, list[str]]

+
+
Returns:
+

(is_valid, list_of_missing_items_with_paths)

+
+
+
+ +
+ +
+
+class scitex.writer.SupplementaryTree(root, git_root=None, contents=None, base=None, supplementary=None, supplementary_diff=None, readme=None, archive=None)[source]
+

Bases: object

+

Supplementary directory structure (02_supplementary/).

+
+
+root: Path
+
+ +
+
+git_root: Path | None = None
+
+ +
+
+contents: SupplementaryContents = None
+
+ +
+
+base: DocumentSection = None
+
+ +
+
+supplementary: DocumentSection = None
+
+ +
+
+supplementary_diff: DocumentSection = None
+
+ +
+
+readme: DocumentSection = None
+
+ +
+
+archive: Path = None
+
+ +
+
+__post_init__()[source]
+

Initialize all instances.

+
+ +
+
+verify_structure()[source]
+

Verify supplementary structure has required components.

+
+
Return type:
+

tuple[bool, list[str]]

+
+
Returns:
+

(is_valid, list_of_missing_items_with_paths)

+
+
+
+ +
+ +
+
+class scitex.writer.RevisionTree(root, git_root=None, contents=None, base=None, revision=None, readme=None, archive=None, docs=None)[source]
+

Bases: object

+

Revision directory structure (03_revision/).

+
+
+root: Path
+
+ +
+
+git_root: Path | None = None
+
+ +
+
+contents: RevisionContents = None
+
+ +
+
+base: DocumentSection = None
+
+ +
+
+revision: DocumentSection = None
+
+ +
+
+readme: DocumentSection = None
+
+ +
+
+archive: Path = None
+
+ +
+
+docs: Path = None
+
+ +
+
+__post_init__()[source]
+

Initialize all instances.

+
+ +
+
+verify_structure()[source]
+

Verify revision structure has required components.

+
+
Return type:
+

tuple[bool, list[str]]

+
+
Returns:
+

(is_valid, list_of_missing_items_with_paths)

+
+
+
+ +
+ +
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/cli.html b/src/scitex/_sphinx_html/source/cli.html new file mode 100644 index 000000000..7c5b410ea --- /dev/null +++ b/src/scitex/_sphinx_html/source/cli.html @@ -0,0 +1,752 @@ + + + + + + + + + CLI Reference — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

CLI Reference

+

SciTeX provides a unified command-line interface. All commands follow the +pattern:

+
scitex <module> <command> [options]
+
+
+

Run scitex --help-recursive to see every available command.

+
+

General

+
scitex --help-recursive        # Show all commands across all modules
+scitex list-python-apis        # List all Python APIs (210 items)
+scitex mcp list-tools          # List all MCP tools (120+ tools)
+
+
+
+
+

Scholar

+

Literature search, PDF management, and metadata enrichment.

+
scitex scholar fetch <DOI>     # Download paper by DOI
+scitex scholar bibtex <file>   # Enrich BibTeX file with metadata
+scitex scholar search <query>  # Search for papers
+
+
+
+
+

Stats

+

Statistical testing with automatic test recommendation.

+
scitex stats recommend         # Suggest appropriate statistical tests
+scitex stats run <test> <data> # Run a specific test on data
+
+
+
+
+

Audio

+

Text-to-speech and audio playback.

+
scitex audio speak <text>      # Convert text to speech
+scitex audio play <file>       # Play an audio file
+
+
+
+
+

Capture

+

Screen capture and monitoring.

+
scitex capture snap            # Take a screenshot
+scitex capture monitor         # Start screen monitoring
+
+
+
+
+

Template

+

Project scaffolding from built-in templates.

+
scitex template clone <type> <name>
+
+
+

Available template types:

+
    +
  • research – Research project with session tracking

  • +
  • pip – Python pip package

  • +
  • paper – LaTeX manuscript project

  • +
  • app – SciTeX app with bridge integration

  • +
  • singularity – Singularity container definition

  • +
+
+
+

Introspect

+

Python code introspection for exploring APIs.

+
scitex introspect api <module>       # List APIs for a module
+scitex introspect source <function>  # View source code of a function
+
+
+
+
+

Dev

+

Ecosystem development and version management.

+
scitex dev versions            # Show versions of all installed packages
+scitex dev ecosystem           # Manage ecosystem packages
+
+
+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/concepts.html b/src/scitex/_sphinx_html/source/concepts.html new file mode 100644 index 000000000..e520c7b18 --- /dev/null +++ b/src/scitex/_sphinx_html/source/concepts.html @@ -0,0 +1,902 @@ + + + + + + + + + Core Concepts — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Core Concepts

+

This page explains the design patterns behind SciTeX. Understanding +these patterns will help you use the toolkit effectively and extend it +for your own workflows.

+
+

Modular Architecture

+

SciTeX is not a monolith. Each module is backed by an independent +package that can be installed and used on its own:

+
pip install scitex-io       # Use standalone: import scitex_io
+pip install scitex-stats    # Use standalone: import scitex_stats
+pip install figrecipe       # Use standalone: import figrecipe
+
+
+

When you install scitex, these packages are available under a +unified namespace:

+
import scitex as stx
+
+stx.io.save(data, "out.csv")     # scitex-io
+stx.stats.run_test(...)          # scitex-stats
+stx.plt.subplots()               # figrecipe
+
+
+

Lazy loading ensures that importing scitex does not pull in every +dependency. Modules are loaded only when first accessed. If you never +touch stx.scholar, its dependencies are never imported and startup +remains fast.

+

The main scitex package itself contains no runtime logic for I/O, +statistics, or plotting. It is an orchestrator: it re-exports +sub-package APIs, provides the CLI entry point, hosts the MCP server, +and owns the session decorator.

+
+
+

Session Decorator

+

The @stx.session decorator converts a plain Python function into a +reproducible, CLI-enabled experiment.

+
import scitex as stx
+
+@stx.session
+def main(
+    n_epochs=50,
+    learning_rate=0.001,
+    CONFIG=stx.session.INJECTED,
+    logger=stx.session.INJECTED,
+):
+    logger.info(f"Training for {n_epochs} epochs at lr={learning_rate}")
+    return 0
+
+if __name__ == "__main__":
+    main()
+
+
+

Why it exists. Research scripts tend to accumulate boilerplate: +argument parsing, output directory creation, logging setup, random seed +management, config file loading. The session decorator handles all of +these so that the function body contains only experiment logic.

+

What it provides:

+
    +
  1. Automatic CLI. Function parameters become command-line arguments. +n_epochs=50 becomes --n-epochs 50 with no argparse code.

  2. +
  3. Config injection. Parameters in ./config/*.yaml are aggregated +into a single CONFIG dict and injected at runtime.

  4. +
  5. Reproducibility. Random seeds are fixed. All parameters, stdout, +and stderr are logged to the output directory.

  6. +
  7. Output organization. Each run gets a timestamped directory under +script_out/:

    +
    script_out/FINISHED_SUCCESS/2026-03-18_14-30-00_AbC1/
    ++-- CONFIGS/CONFIG.yaml    # All parameters
    ++-- logs/stdout.log        # Captured output
    ++-- logs/stderr.log        # Captured errors
    +
    +
    +
  8. +
+

Parameters marked stx.session.INJECTED are sentinel values. The +decorator replaces them with runtime objects (logger, config, plotting +backend, color palette, random generator). You never pass these +yourself – they signal to the decorator what to inject.

+
+
+

Unified I/O

+

stx.io.save and stx.io.load provide a single interface for 30+ +file formats. The format is determined by the file extension:

+
stx.io.save(dataframe, "results.csv")
+stx.io.save(array, "weights.npy")
+stx.io.save(fig, "figure.png")
+stx.io.save(config, "params.yaml")
+
+
+

The same call pattern works for every type. No need to remember +pd.to_csv, np.save, fig.savefig, or yaml.dump.

+

Figure data export. When saving a matplotlib figure, SciTeX +automatically exports the plotted data as a CSV file alongside the +image. This is a deliberate design choice: every figure in a publication +should be backed by accessible data, so that reviewers and readers can +verify the plot independently.

+
stx.io.save(fig, "plot.png")
+# Creates: plot.png  (the image)
+#          plot.csv  (the underlying data)
+
+
+

This behavior is powered by figrecipe’s RecordingFigure, which +tracks all data passed to plotting calls and serializes it on save.

+
+
+

Three Interfaces

+

Every SciTeX feature is accessible through three interfaces:

+
    +
  1. Python API – for scripts and notebooks

  2. +
  3. CLI – for shell workflows and automation

  4. +
  5. MCP – for AI agents via the Model Context Protocol

  6. +
+

For example, running a statistical test:

+
# Python API
+result = stx.stats.run_test("ttest_ind", group1, group2)
+
+
+
# CLI
+scitex stats run ttest_ind --data data.csv
+
+
+
# MCP tool (called by AI agents)
+stats_run_test(test="ttest_ind", data=[[1,2,3],[4,5,6]])
+
+
+

This three-interface design means that any workflow a human builds in +Python can be replicated by an AI agent through MCP, or scripted in a +shell pipeline through the CLI. The interfaces share the same +underlying implementation, so behavior is identical across all three.

+

The MCP server aggregates 120+ tools from all sub-packages. AI agents +can discover available tools, read documentation, and execute full +research pipelines – from literature search to manuscript compilation – +without human intervention.

+
+
+

Delegation Pattern

+

The scitex package is intentionally thin. It delegates all domain +logic to standalone packages:

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

scitex module

Standalone package

Responsibility

stx.io

scitex-io

File I/O (30+ formats)

stx.stats

scitex-stats

Statistical testing (23 tests)

stx.plt

figrecipe

Publication-ready figures

stx.writer

scitex-writer

LaTeX manuscript compilation

stx.scholar

scitex-scholar

Literature search, PDF download

stx.dataset

scitex-dataset

Scientific dataset access

+

Why delegation? Each sub-package has its own release cycle, test +suite, and dependency tree. A bug fix in scitex-io does not require +releasing all of scitex. Users who only need file I/O can +pip install scitex-io without pulling in matplotlib, LaTeX tooling, +or audio dependencies.

+

From the user’s perspective, the delegation is invisible. A single +import scitex as stx provides access to everything. The lazy loader +in scitex.__init__ resolves stx.io to the scitex_io package +on first access.

+
+
+

Configuration

+

SciTeX uses a layered configuration system:

+

Project-level config (YAML files)

+

Place YAML files in ./config/ at your project root. The session +decorator aggregates all files in this directory into a single CONFIG +dict:

+
config/
++-- model.yaml      # {"hidden_size": 256, "n_layers": 4}
++-- training.yaml   # {"epochs": 100, "batch_size": 32}
+
+
+
@stx.session
+def main(CONFIG=stx.session.INJECTED):
+    print(CONFIG["hidden_size"])  # 256
+    print(CONFIG["epochs"])       # 100
+
+
+

Environment-level config (.env.d/)

+

Credentials, API keys, and machine-specific paths live in .env.d/:

+
.env.d/
++-- entry.src            # Single entry point (source this)
++-- 00_scitex.env        # Base paths
++-- 01_scholar.env       # OpenAthens credentials
++-- 01_audio.env         # TTS backend config
+
+
+

Source the entry point in your shell profile:

+
# In ~/.bashrc or ~/.zshrc
+source /path/to/.env.d/entry.src
+
+
+

MCP environment (.src files)

+

For AI agent deployments, a single .src file bundles all environment +variables. The MCP server loads it at startup via SCITEX_ENV_SRC:

+
export SCITEX_ENV_SRC=~/.scitex/scitex/local.src
+
+
+

This keeps the .mcp.json configuration static across machines – +only the .src file changes between local and remote environments.

+

The configuration hierarchy is: CLI arguments override YAML config, +which overrides environment variables, which override built-in defaults.

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/ecosystem.html b/src/scitex/_sphinx_html/source/ecosystem.html new file mode 100644 index 000000000..7fc78d033 --- /dev/null +++ b/src/scitex/_sphinx_html/source/ecosystem.html @@ -0,0 +1,813 @@ + + + + + + + + + Ecosystem — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Ecosystem

+

SciTeX is a modular toolkit composed of standalone packages. Each package +can be installed and used independently, or accessed through the unified +import scitex interface.

+
+

Delegation Pattern

+

The scitex package itself contains no runtime logic. It acts as an +orchestrator that re-exports functionality from sub-packages via lazy +imports. When you write scitex.io.load("data.csv"), the call is +delegated to the scitex_io package under the hood.

+

This means:

+
    +
  • Standalone use: pip install scitex-io then import scitex_io

  • +
  • Unified use: pip install scitex[io] then import scitex; scitex.io.load(...)

  • +
+

Both paths execute the same code. The scitex package simply provides +the namespace glue.

+
# These are equivalent:
+import scitex_io
+scitex_io.load("data.csv")
+
+import scitex
+scitex.io.load("data.csv")
+
+
+
+
+

Package Reference

+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Package

scitex Module

PyPI

Description

scitex-io

scitex.io

pip install scitex-io

Unified file I/O for 30+ formats

scitex-stats

scitex.stats

pip install scitex-stats

Publication-ready statistics (23+ tests)

figrecipe

scitex.plt

pip install figrecipe

Publication-ready matplotlib figures

scitex-writer

scitex.writer

pip install scitex-writer

LaTeX manuscript compilation

scitex-scholar

scitex.scholar

pip install scitex-scholar

Literature search and management

scitex-audio

scitex.audio

pip install scitex-audio

Text-to-speech and audio

scitex-dev

scitex.dev

pip install scitex-dev

Developer tools, ecosystem management

scitex-clew

scitex.clew

pip install scitex-clew

Hash-based reproducibility verification

scitex-linter

scitex.linter

pip install scitex-linter

AST-based code pattern checking

scitex-dataset

scitex.dataset

pip install scitex-dataset

Scientific dataset access (DANDI, OpenNeuro, PhysioNet)

crossref-local

scitex.scholar.crossref

pip install crossref-local

Local CrossRef database (167M+ papers)

openalex-local

scitex.scholar.openalex

pip install openalex-local

Local OpenAlex database (250M+ papers)

socialia

scitex.social

pip install socialia

Social media posting (Twitter, LinkedIn)

scitex-app

scitex.app

pip install scitex-app

Runtime SDK for SciTeX apps

scitex-cloud

scitex.cloud

pip install scitex-cloud

Cloud platform integration

scitex-notification

scitex.notify

pip install scitex-notification

Multi-backend notifications

+
+
+

Installation

+

Install individual packages or use extras through the main package:

+
# Full installation (all packages)
+pip install scitex[all]
+
+# Typical research setup
+pip install scitex[plt,stats,scholar]
+
+# Individual standalone package
+pip install figrecipe
+
+
+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/gallery.html b/src/scitex/_sphinx_html/source/gallery.html new file mode 100644 index 000000000..0ba421453 --- /dev/null +++ b/src/scitex/_sphinx_html/source/gallery.html @@ -0,0 +1,1018 @@ + + + + + + + + + Plot Gallery — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ + + + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/index.html b/src/scitex/_sphinx_html/source/index.html new file mode 100644 index 000000000..a3a042b0c --- /dev/null +++ b/src/scitex/_sphinx_html/source/index.html @@ -0,0 +1,877 @@ + + + + + + + + + SciTeX Documentation — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

SciTeX Documentation

+

SciTeX is a modular Python toolkit for research automation. It provides a +unified interface across the full scientific workflow: from data loading and +statistical analysis, through publication-ready plotting and manuscript +compilation, to AI-assisted literature review. Every module is installable +independently, and the entire surface area is exposed to AI agents through +an MCP (Model Context Protocol) server.

+
+

Key Features

+
    +
  • Session decorator – wrap any experiment function with @stx.session +to get automatic directory management, reproducibility logging, and +error-safe cleanup.

  • +
  • Unified I/Ostx.io.save / stx.io.load handle 40+ formats +(CSV, HDF5, YAML, images, PDFs, …) through a single call.

  • +
  • Statisticsstx.stats provides hypothesis testing, effect sizes, +power analysis, and multiple-comparison correction with APA-formatted +output.

  • +
  • Plottingstx.plt produces publication-ready figures via +figrecipe, with +millimetre-based layouts, journal style presets, and automatic CSV data +export alongside every saved figure.

  • +
  • Manuscript writingstx.writer compiles LaTeX manuscripts, +manages BibTeX, and exports to Overleaf.

  • +
  • Literature managementstx.scholar searches CrossRef, OpenAlex, +and Google Scholar; downloads and parses PDFs; and maintains a local +citation database.

  • +
  • MCP for AI agents – every capability above is available as a +tool call through the built-in MCP server, so LLM agents can run +statistics, create figures, and compile papers programmatically.

  • +
+
+
+

Getting Started

+
    +
  1. Installation – install the core package and choose the extras you +need.

  2. +
  3. Quickstart – a five-minute walkthrough of the session decorator, +I/O, and plotting.

  4. +
  5. API Reference – full API reference generated from docstrings.

  6. +
+ + + +
+

Reference

+ +
+
+
+
+

Indices and tables

+ +
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/installation.html b/src/scitex/_sphinx_html/source/installation.html new file mode 100644 index 000000000..b2dd2f48e --- /dev/null +++ b/src/scitex/_sphinx_html/source/installation.html @@ -0,0 +1,840 @@ + + + + + + + + + Installation — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Installation

+
+

Requirements

+
    +
  • Python 3.10+

  • +
  • uv (strongly recommended) — install with pip install uv or +curl -LsSf https://astral.sh/uv/install.sh | sh

  • +
  • pip 21+ also works, but expect 30–90 min for scitex[all] +(see warning below)

  • +
+
+

Warning

+

pip install "scitex[all]" typically takes 30–90 minutes because +pip’s serial resolver walks version histories of the large transitive +dependency set (numpy/pandas/torch/jax/playwright/openalex-local/…). +Use uv instead — it resolves the same set in parallel in 1–3 +minutes. Every pip install line on this page also works as +uv pip install and we recommend the uv form.

+
+
+
+

Core Install

+

The base package pulls in only lightweight dependencies and gives you access +to session management, path utilities, string helpers, and the module +discovery system.

+
uv pip install scitex          # recommended
+pip install scitex              # also works, slower for [all]
+
+
+
+ +
+

Research Workflow

+

For a typical research project you need figures, statistics, and literature +search but not audio, browser automation, or cloud tools:

+
pip install "scitex[plt,stats,scholar]"
+
+
+
+
+

Per-Module Extras

+

Install only what you need. Each extra maps to a self-contained capability.

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Extra

Description

plt

Publication-ready figures via figrecipe (matplotlib, seaborn, Pillow)

stats

Hypothesis testing, effect sizes, power analysis (scitex-stats, scipy, statsmodels)

io

Unified I/O for 40+ formats (HDF5, Excel, YAML, PDF, images, …)

scholar

Literature search and PDF management (CrossRef, OpenAlex, Semantic Scholar)

writer

LaTeX manuscript compilation, BibTeX management, Overleaf export

audio

Text-to-speech and audio utilities (scitex-audio)

ai

LLM APIs (OpenAI, Anthropic, Google, Groq) and ML tools (scikit-learn)

browser

Web automation via Playwright

capture

Screenshot capture (mss, Playwright)

dataset

Scientific dataset access (DANDI, OpenNeuro, PhysioNet)

cloud

Cloud integration utilities

app

Unified file storage SDK (scitex-app)

session

Session decorator with reproducibility logging

diagram

Diagram generation (Mermaid, Graphviz)

db

Database access (SQLAlchemy, PostgreSQL)

cv

Computer vision (OpenCV, Pillow)

dsp

Digital signal processing (scipy, tensorpac)

social

Social media posting (socialia)

tunnel

SSH tunnel management (scitex-tunnel)

all

Everything above

+

Example combinations:

+
# Neuroscience analysis
+pip install "scitex[plt,stats,dsp,dataset]"
+
+# Paper writing
+pip install "scitex[writer,scholar,plt]"
+
+# AI agent development
+pip install "scitex[ai,browser,capture]"
+
+
+
+
+

Development Install

+

Clone the repository and install in editable mode with development tools:

+
git clone https://github.com/ywatanabe1989/scitex-python.git
+cd scitex-python
+pip install -e ".[dev]"
+
+
+

The dev extra includes pytest, ruff, mypy, Sphinx, and build tools.

+ +
+
+

Verifying the Installation

+
import scitex as stx
+print(stx.__version__)
+
+
+

To check which optional modules are available:

+
stx.usage.list()
+
+
+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/mcp.html b/src/scitex/_sphinx_html/source/mcp.html new file mode 100644 index 000000000..acdfe9c15 --- /dev/null +++ b/src/scitex/_sphinx_html/source/mcp.html @@ -0,0 +1,832 @@ + + + + + + + + + MCP Server — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

MCP Server

+
+

What is MCP?

+

The Model Context Protocol (MCP) is +an open protocol that lets AI agents call external tools through a +standardized interface. Instead of generating code and hoping it works, an +agent can invoke a well-defined tool and receive structured results.

+

SciTeX implements an MCP server that exposes 120+ tools covering the full +research workflow. Any MCP-compatible client – Claude Code, Cursor, or +custom agents – can use these tools to conduct literature searches, run +statistics, create figures, and compile manuscripts.

+
+
+

Setup

+

Add the following to .mcp.json in your project root:

+
{
+  "mcpServers": {
+    "scitex": {
+      "command": "scitex",
+      "args": ["mcp", "start"],
+      "env": {
+        "SCITEX_ENV_SRC": "${SCITEX_ENV_SRC}"
+      }
+    }
+  }
+}
+
+
+

Then set the environment source file in your shell profile:

+
# Local machine
+export SCITEX_ENV_SRC=~/.scitex/scitex/local.src
+
+# Remote server
+export SCITEX_ENV_SRC=~/.scitex/scitex/remote.src
+
+
+

Generate a template .src file:

+
scitex env-template -o ~/.scitex/scitex/local.src
+
+
+

Or install the MCP server globally:

+
scitex mcp installation
+
+
+
+
+

Tool Categories

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Category

Tools

Description

writer

28

LaTeX manuscript compilation

scholar

23

PDF download, metadata enrichment

capture

12

Screen monitoring and capture

introspect

12

Python code introspection

audio

10

Text-to-speech, audio playback

stats

10

Automated statistical testing

plt

9

Matplotlib figure creation

diagram

9

Mermaid and Graphviz diagrams

dataset

8

Scientific dataset access

social

7

Social media posting

canvas

7

Scientific figure canvas

template

6

Project scaffolding

verify

6

Reproducibility verification

dev

6

Ecosystem version management

ui

5

Notifications

linter

3

Code pattern checking

+

All tools accept JSON parameters and return structured results.

+
+
+

Example Workflow

+

A typical AI-driven research workflow chains tools across categories:

+
    +
  1. Scholar – Search literature and download papers

    +
    scholar_search_papers(query="neural oscillations gamma band")
    +scholar_fetch_papers(dois=["10.1038/s41586-024-..."])
    +
    +
    +
  2. +
  3. Stats – Analyze experimental data

    +
    stats_recommend_tests(data=[[1.2, 3.4, ...], [2.1, 4.5, ...]])
    +stats_run_test(test="mann_whitney_u", groups=[[...], [...]])
    +
    +
    +
  4. +
  5. Plt – Create publication-ready figures

    +
    plt_bar(data={"Control": [1,2,3], "Treatment": [4,5,6]},
    +        ylabel="Response", title="Figure 1")
    +
    +
    +
  6. +
  7. Writer – Compile the manuscript

    +
    writer_compile_manuscript(project="my_paper")
    +
    +
    +
  8. +
+

The agent orchestrates these steps autonomously, passing outputs from one +tool as inputs to the next.

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/scitex/_sphinx_html/source/quickstart.html b/src/scitex/_sphinx_html/source/quickstart.html new file mode 100644 index 000000000..ffe15cd17 --- /dev/null +++ b/src/scitex/_sphinx_html/source/quickstart.html @@ -0,0 +1,922 @@ + + + + + + + + + Quickstart — SciTeX v0.0.0 + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Quickstart

+

This guide covers the most common SciTeX workflows with working code +examples. For design rationale, see Core Concepts.

+
+

Session Decorator

+

The @stx.session decorator is the recommended entry point for any +SciTeX script. It converts your function into a self-contained, +reproducible experiment with automatic CLI generation, config loading, +logging, and organized output directories.

+
import scitex as stx
+
+@stx.session
+def main(
+    n_samples=100,
+    CONFIG=stx.session.INJECTED,
+    plt=stx.session.INJECTED,
+    logger=stx.session.INJECTED,
+):
+    """Generate sample data and plot."""
+    import numpy as np
+
+    x = np.linspace(0, 2 * np.pi, n_samples)
+    y = np.sin(x)
+
+    fig, ax = stx.plt.subplots()
+    ax.plot_line(x, y)
+    ax.set_xyt("Time", "Amplitude", "Sine Wave")
+    stx.io.save(fig, "sine.png")  # Saves sine.png + sine.csv
+    return 0
+
+if __name__ == "__main__":
+    main()
+
+
+

Run from the command line – arguments are generated automatically from +the function signature:

+
python script.py --n-samples 200
+
+
+

Parameters marked with stx.session.INJECTED are provided by the +session runtime. You never pass them yourself:

+
    +
  • CONFIG – aggregated YAML parameters from ./config/*.yaml

  • +
  • plt – matplotlib wrapped by figrecipe

  • +
  • logger – colored, file-backed logger

  • +
  • COLORS – publication color palette

  • +
  • rngg – seeded random number generator

  • +
+

Output is organized into a timestamped directory:

+
script_out/FINISHED_SUCCESS/2026-03-18_14-30-00_AbC1/
++-- sine.png               # Figure with embedded metadata
++-- sine.csv               # Auto-exported plot data
++-- CONFIGS/CONFIG.yaml    # Reproducible parameters
++-- logs/stdout.log        # Standard output
++-- logs/stderr.log        # Standard error
+
+
+
+
+

Unified File I/O

+

stx.io.save and stx.io.load handle 30+ formats through a single +interface. The format is inferred from the file extension.

+
import scitex as stx
+import numpy as np
+import pandas as pd
+
+# DataFrame -- CSV
+df = pd.DataFrame({"x": [1, 2, 3], "y": [4, 5, 6]})
+stx.io.save(df, "data.csv")
+df = stx.io.load("data.csv")
+
+# NumPy array -- NPY
+arr = np.random.randn(100, 50)
+stx.io.save(arr, "array.npy")
+arr = stx.io.load("array.npy")
+
+# Figure -- PNG (also generates CSV of plotted data)
+fig, ax = stx.plt.subplots()
+ax.plot_line([1, 2, 3, 4, 5])
+stx.io.save(fig, "plot.png")  # Creates plot.png AND plot.csv
+
+# Dictionary -- YAML
+params = {"learning_rate": 0.001, "epochs": 100}
+stx.io.save(params, "params.yaml")
+params = stx.io.load("params.yaml")
+
+# Dictionary -- JSON
+metadata = {"subject": "S01", "task": "rest"}
+stx.io.save(metadata, "meta.json")
+metadata = stx.io.load("meta.json")
+
+# Arbitrary object -- Pickle
+stx.io.save({"complex": [1, 2, 3]}, "data.pkl")
+obj = stx.io.load("data.pkl")
+
+
+

When saving a matplotlib figure, SciTeX automatically exports the +underlying data as a CSV file alongside the image. This ensures every +figure in a paper can be independently verified and reproduced.

+
+
+

Statistical Analysis

+

SciTeX wraps 23 statistical tests with a consistent interface that +includes effect sizes, confidence intervals, and power analysis.

+
import scitex as stx
+import numpy as np
+
+group1 = np.random.randn(30) + 0.5
+group2 = np.random.randn(30)
+
+# Run a t-test with full reporting
+result = stx.stats.run_test(
+    "ttest_ind", group1, group2, return_as="dataframe"
+)
+print(result)
+# Columns: test, statistic, p_value, effect_size, ci_lower, ci_upper, power
+
+
+

Not sure which test to use? Let SciTeX recommend one:

+
data = {"group_a": group1, "group_b": group2}
+recommendations = stx.stats.recommend_tests(data)
+print(recommendations)
+
+
+

Additional utilities:

+
# Multiple comparison correction
+corrected = stx.stats.correct_pvalues([0.01, 0.04, 0.06], method="bonferroni")
+
+# Effect size calculation
+d = stx.stats.effect_size(group1, group2, test="cohens_d")
+
+# Format results for publication
+formatted = stx.stats.format_results(result)
+# "t(58) = 2.34, p = .021, d = 0.60"
+
+
+
+
+

Publication-Ready Figures

+

SciTeX delegates to figrecipe +for publication-quality matplotlib figures with a consistent API.

+

Basic line plot

+
import scitex as stx
+import numpy as np
+
+x = np.linspace(0, 10, 200)
+
+fig, ax = stx.plt.subplots()
+ax.plot_line(x, np.sin(x))
+ax.set_xyt("Time (s)", "Amplitude", "Sine Wave")
+stx.io.save(fig, "line.png")
+
+
+

Multi-panel figure

+
import scitex as stx
+import numpy as np
+
+fig, axes = stx.plt.subplots(1, 3)
+
+# Panel A: line plot
+x = np.linspace(0, 10, 200)
+axes[0].plot_line(x, np.sin(x))
+axes[0].set_xyt("Time", "Value", "Line")
+
+# Panel B: violin plot
+data = [np.random.randn(50) + i for i in range(3)]
+axes[1].stx_violin(data)
+axes[1].set_xyt("Group", "Value", "Violin")
+
+# Panel C: heatmap
+matrix = np.random.randn(10, 10)
+axes[2].stx_heatmap(matrix)
+axes[2].set_xyt("X", "Y", "Heatmap")
+
+stx.io.save(fig, "panels.png")
+
+
+

Statistical visualization

+
import scitex as stx
+import numpy as np
+
+data = np.random.randn(100, 50)
+
+fig, ax = stx.plt.subplots()
+ax.stx_mean_std(data)
+ax.set_xyt("Time", "Value", "Mean +/- SD")
+
+stx.io.save(fig, "stats_plot.png")
+
+
+
+
+

Literature Management

+

The scholar module handles paper discovery, PDF downloads, and BibTeX +enrichment.

+

CLI usage

+
# Enrich a BibTeX file with missing metadata (DOIs, abstracts)
+scitex scholar bibtex refs.bib
+
+# Fetch a paper by DOI
+scitex scholar fetch "10.1038/s41586-024-07487-w"
+
+# Search for papers
+scitex scholar search "deep learning EEG" --limit 20
+
+
+

Python API

+
import scitex as stx
+
+# Search for papers
+results = stx.scholar.search("transformer attention mechanism", limit=10)
+
+# Parse and enrich a BibTeX file
+entries = stx.scholar.parse_bibtex("refs.bib")
+enriched = stx.scholar.enrich_bibtex("refs.bib")
+
+
+
+
+

CLI Usage

+

SciTeX provides a unified CLI that mirrors the Python module structure.

+
# Show all available commands
+scitex --help-recursive
+
+# Statistics
+scitex stats recommend             # Suggest tests for your data
+scitex stats run ttest_ind         # Run a specific test
+
+# Scholar / Literature
+scitex scholar fetch "10.1038/..." # Download paper by DOI
+scitex scholar bibtex refs.bib     # Enrich BibTeX metadata
+scitex scholar search "query"      # Search for papers
+
+# Figures and diagrams
+scitex plt info                    # Show available plot types
+
+# Utilities
+scitex audio speak "Analysis complete"  # Text-to-speech notification
+scitex capture snap                     # Take a screenshot
+
+# Introspection
+scitex introspect api scitex.stats      # List APIs for a module
+scitex list-python-apis                 # List all Python APIs
+scitex mcp list-tools                   # List all MCP tools
+
+
+

Every CLI command corresponds to a Python function and an MCP tool. +See Core Concepts for details on this three-interface design.

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2024-2026, Yusuke Watanabe.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file