Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

README.md

PySNT Development Tools

Development tools and templates for PySNT.

Overview

  • Curate vs Extended classes: In theory, all SNT classes would be treated the same. In practice, we split them in two groups:
    • Curate classes: Most commonly used. These are 1st class citizens with functional autocompletion
    • Extended classes: These are either 'problematic' classes (e.g., objects that cannot be constructed in headless env.). These won't have much of auto-completion. Users need to use java_utils to explore extended classes:
      import pysnt
      pysnt.initialize()
      # Get all methods from TreeStatistics
      methods = pysnt.get_methods('TreeStatistics')
      # Find all members containing 'length'
      results = pysnt.find_members('TreeStatistics', 'length')
      # Get static fields (constants)
      constants = pysnt.get_fields('TreeStatistics', static_only=True)
  • Package names: Follow Java package structure (sc.fiji.snt.analysis.morphology)
  • Module paths: Follow Python import structure (pysnt.analysis.morphology)
  • Class names: Use exact Java class names. Reflection converters use underscore notation (OuterClass_InnerClass)
  • Module docstring: Include link to Javadoc package summary
  • placeholder_template.py: Is the template for creating new PySNT module __init__.py files with consistent structure
  • See the dev/scripts/ directory for build and deployment scripts
  • Use dev/scripts/extract_class_signatures.py and dev/scripts/generate_stubs.py for type stub generation

To Add a New SNT Package

Option 1 (Recommended): Using create_module.py

Basic examples:

# Create a basic morphology analysis module
python dev/create_module.py analysis.morphology
# Create nested modules
python dev/create_module.py analysis.morphology.advanced

What this creates:

  • init.py files that now need to be manually edited

Examples with Class Lists:

python dev/create_module.py analysis.morphology \
  --curated Class1Name Class2Name Class3Name \
  --extended Class4Name Class5Name Class6Name

What this creates:

  • Module: src/pysnt/analysis/morphology/__init__.py (Java package: sc.fiji.snt.analysis.morphology)
  • Curated classes (always available): Class1Name, Class2Name, Class3Name
  • Extended classes (on-demand): Class4Name, Class5Name, Class6Name

Examples with Inner Classes:

python dev/create_module.py analysis.morphology \
  --curated Class1Name_Config  \
  --extended Class2Name_Config

What this creates:

  • Module: src/pysnt/analysis/morphology/__init__.py (Java package: sc.fiji.snt.analysis.morphology)
  • Curated classes (always available): Class1Name$Config
  • Extended classes (on-demand): Class2Name$Config

After creating any module:

  • Edit the generated file
  • Update docstrings
  • Remove template sections:
  • Delete the template checklist
  • Generate type stubs:
    python dev/scripts/extract_class_signatures.py --all-classes --verbose  # Extract signatures
    python dev/scripts/generate_stubs.py --verbose --overwrite              # Generate stubs
  • Test the module:
    python -c "import src.pysnt.analysis.morphology; print('Module works!')"

Option 2 (Manually)

  1. Create Module Structure
# Example: Adding morphology analysis module
mkdir -p src/pysnt/analysis/morphology
  1. Copy Template
cp dev/placeholder_template.py src/pysnt/analysis/morphology/__init__.py
  1. Configure Module Edit the new __init__.py file:
  # 1. Update module docstring

  # 2. Fill-in the lists of public classes in the package
  CURATED_CLASSES = [
      "MorphologyAnalyzer",  # Most important classes
      "ShapeMetrics",
      "BranchAnalyzer"
  ]

  EXTENDED_CLASSES = [
      "AdvancedMorphology",  # Less common classes
      "DetailedMetrics",
      "InternalUtils"
  ]

  # Set package name
  _module_funcs = setup_module_classes(
      package_name="sc.fiji.snt.analysis.morphology",  # ← Edit this
      curated_classes=CURATED_CLASSES,
      extended_classes=EXTENDED_CLASSES,
      globals_dict=globals(),
  )

  # Set module path
  __getattr__ = _module_funcs['create_getattr']('pysnt.analysis.morphology')  # ← Edit this
  1. Generate Type Stubs
python dev/scripts/extract_class_signatures.py --all-classes --verbose
python dev/scripts/generate_stubs.py --verbose --overwrite
  1. Test
python -c "
import src.pysnt.new.module as mod
print(f'Curated: {mod.get_curated_classes()}')
print(f'Extended: {mod.get_extended_classes()}')
print(f'Total: {len(mod.get_available_classes())} classes')
"
# Generate type stubs and test
python dev/scripts/generate_stubs.py --verbose
python -c "
import src.pysnt.new.module as mod
print('All tests passed!')
print(f'Module ready with {len(mod.get_available_classes())} classes')
"
  1. Update Parent Module (as needed) If adding a submodule, update the parent's __init__.py:
# In parent src/pysnt/analysis/__init__.py
from . import morphology

# Update __getattr__ to handle submodules
__getattr__ = _module_funcs['create_getattr']('pysnt.analysis', submodules=['morphology'])   # ← Edit this

Configuration Options

Basic Configuration

# Minimal setup for simple modules
_module_funcs = setup_module_classes(
    package_name="sc.fiji.snt.new.package",
    curated_classes=CURATED_CLASSES,
    extended_classes=EXTENDED_CLASSES,
    globals_dict=globals(),
)

Advanced Configuration

# Advanced setup with additional options
_module_funcs = setup_module_classes(
    package_name="sc.fiji.snt.new.package",
    curated_classes=CURATED_CLASSES,
    extended_classes=EXTENDED_CLASSES,
    globals_dict=globals(),
    discovery_packages=["sc.fiji.snt", "sc.fiji.snt.util"],  # Search multiple packages
    include_interfaces=True,  # Include Java interfaces
)

Submodule Support

# For modules with submodules
from . import submodule1, submodule2

__getattr__ = _module_funcs['create_getattr']('pysnt.new.module', submodules=['submodule1', 'submodule2'])

Constructor Exception Handling Guide

The handle_constructor_exceptions() function in src/pysnt/common_module.py provides a centralized way to handle Java constructor issues that arise from the Python-Java bridge.

Common Issues

  1. Varargs Constructor Selection Problem: Python calls Constructor(args, String...) with empty varargs instead of Constructor(args) Example: MultiTreeStatistics(Collection) vs MultiTreeStatistics(Collection, String...)

  2. Overloaded Constructor Ambiguity Problem: Python can't distinguish between similar constructor signatures Example: Multiple constructors with different parameter types

  3. Parameter Type Conversion Problem: Python types don't map correctly to expected Java types Example: Python list vs Java Collection vs Java Array

Adding New Exception Handlers

To add a new constructor exception handler, modify the handle_constructor_exceptions() function:

def handle_constructor_exceptions(java_class, class_name, *args, **kwargs):
    # Existing MultiTreeStatistics case
    if class_name == 'MultiTreeStatistics' and len(args) == 1 and len(kwargs) == 0:
        return java_class(args[0], "all")

    # NEW CASE: Add another case here
    if class_name == 'TheProblematicClass' and the_condition:
        # return fix here
        return java_class(modified_args)

    # No special handling needed
    return None