From d4fea837e61cdc9aae72cd3e70e8a16a748ffb43 Mon Sep 17 00:00:00 2001 From: jgilber2 Date: Fri, 1 May 2026 09:22:44 -0700 Subject: [PATCH 1/3] Added basic claude instructions for clean pre-commits --- CLAUDE.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..8ef04a7 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,17 @@ +# Claude Instructions for circepy + +## Python Environment +- Always use virtualenv for Python operations (don't rely on system Python or unauthenticated pip installs) +- Activate the virtual environment before running Python commands or installing packages + +## Pre-completion Checklist +Before completing any task, run git pre-commit checks: +```bash +git pre-commit run --all-files +``` + +If pre-commit checks fail, fix the issues and re-run until they pass. + +## Git Workflow +- Do not run `git commit` — the user will handle commits +- Run pre-commit checks to validate code quality before marking tasks complete From fb24f580ae0c2de9e401af39c80b8d3e4bd0b1af Mon Sep 17 00:00:00 2001 From: jgilber2 Date: Fri, 1 May 2026 09:41:56 -0700 Subject: [PATCH 2/3] Added instructions on testing to keep agents targeted on their tasks --- CLAUDE.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 8ef04a7..50a1d49 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,8 +4,28 @@ - Always use virtualenv for Python operations (don't rely on system Python or unauthenticated pip installs) - Activate the virtual environment before running Python commands or installing packages +## Starting tasks - record testing state + +At the start of any task, record the state of tests as a baseline. It is not your job to fix pre-existing issues unless otherwise specified. + +Run tests with multiprocess for speed and store the state: +```bash +pytest -n auto --tb=short -v --json-report --json-report-file=.test_baseline.json +``` + +If the test state file is not created, check that pytest-xdist and pytest-json-report are installed in the virtualenv. + ## Pre-completion Checklist -Before completing any task, run git pre-commit checks: +Before completing any task: + +1. Re-run pytest to verify no regressions: +```bash +pytest -n auto --tb=short -v --json-report --json-report-file=.test_final.json +``` + +Compare `.test_baseline.json` with `.test_final.json` — the final state should not show new failures. + +2. Run git pre-commit checks: ```bash git pre-commit run --all-files ``` From 44ddacc1434da42aa59e6423822ffab56f3bdf77 Mon Sep 17 00:00:00 2001 From: jgilber2 Date: Fri, 1 May 2026 09:51:46 -0700 Subject: [PATCH 3/3] Code cleanup --- .../checkers/base_corelated_criteria_check.py | 20 ++++++++-------- circe/check/checkers/comparisons.py | 4 ++-- .../check/checkers/death_time_window_check.py | 4 +++- circe/check/checkers/drug_era_check.py | 8 ++++--- circe/check/checkers/exit_criteria_check.py | 4 +++- .../exit_criteria_days_offset_check.py | 4 +++- circe/check/checkers/initial_event_check.py | 4 +++- .../check/checkers/no_exit_criteria_check.py | 4 +++- circe/check/checkers/ocurrence_check.py | 4 ++-- circe/check/checkers/range_check.py | 4 ++-- circe/check/checkers/range_checker_factory.py | 8 +++---- circe/check/checkers/time_window_check.py | 4 ++-- circe/check/utils/criteria_name_helper.py | 2 +- circe/cohortdefinition/builders/base.py | 2 -- .../builders/visit_occurrence.py | 2 -- circe/cohortdefinition/code_generator.py | 2 +- .../cohort_expression_query_builder.py | 4 ++-- circe/cohortdefinition/criteria.py | 10 +------- .../printfriendly/markdown_render.py | 2 +- circe/execution/lower/condition_era.py | 4 ++-- circe/execution/lower/dose_era.py | 4 ++-- circe/execution/lower/drug_era.py | 4 ++-- circe/execution/normalize/cohort.py | 4 ++-- circe/extensions/__init__.py | 24 +++++++++---------- .../builders/waveform_channel_metadata.py | 2 +- .../waveform/builders/waveform_feature.py | 2 +- .../waveform/builders/waveform_occurrence.py | 2 +- .../waveform/builders/waveform_registry.py | 2 +- pyproject.toml | 23 ++++++++++++++++++ 29 files changed, 95 insertions(+), 72 deletions(-) diff --git a/circe/check/checkers/base_corelated_criteria_check.py b/circe/check/checkers/base_corelated_criteria_check.py index 42b8296..bdf1561 100644 --- a/circe/check/checkers/base_corelated_criteria_check.py +++ b/circe/check/checkers/base_corelated_criteria_check.py @@ -44,8 +44,8 @@ def _internal_check(self, expression: "CohortExpression", reporter: WarningRepor if inclusion_rule.expression and inclusion_rule.expression.criteria_list: for criteria in inclusion_rule.expression.criteria_list: # Skip if criteria is still a dict (shouldn't happen after deserialization, but be defensive) - if isinstance(criteria, dict): - continue + if isinstance(criteria, dict): # type: ignore[unreachable] + continue # type: ignore[unreachable] group_name = f"{self.INCLUSION_RULE}{inclusion_rule.name}" self._check_criteria(criteria, group_name, reporter) if hasattr(criteria, "criteria") and criteria.criteria: @@ -60,16 +60,16 @@ def _check_criteria_group(self, criteria: "Criteria", group_name: str, reporter: reporter: The warning reporter to use """ # Skip if criteria is still a dict (not yet deserialized) - if isinstance(criteria, dict): - return + if isinstance(criteria, dict): # type: ignore[unreachable] + return # type: ignore[unreachable] if hasattr(criteria, "correlated_criteria") and criteria.correlated_criteria: correlated = criteria.correlated_criteria if hasattr(correlated, "criteria_list") and correlated.criteria_list: for corelated_criteria in correlated.criteria_list: # Skip dicts - if isinstance(corelated_criteria, dict): - continue + if isinstance(corelated_criteria, dict): # type: ignore[unreachable] + continue # type: ignore[unreachable] self._check_criteria(corelated_criteria, group_name, reporter) if hasattr(corelated_criteria, "criteria") and corelated_criteria.criteria: self._check_criteria_group(corelated_criteria.criteria, group_name, reporter) @@ -78,8 +78,8 @@ def _check_criteria_group(self, criteria: "Criteria", group_name: str, reporter: if hasattr(group, "criteria_list") and group.criteria_list: for corelated_criteria in group.criteria_list: # Skip dicts - if isinstance(corelated_criteria, dict): - continue + if isinstance(corelated_criteria, dict): # type: ignore[unreachable] + continue # type: ignore[unreachable] self._check_criteria(corelated_criteria, group_name, reporter) if hasattr(corelated_criteria, "criteria") and corelated_criteria.criteria: self._check_criteria_group(corelated_criteria.criteria, group_name, reporter) @@ -99,7 +99,7 @@ def _check_criteria( """ # Skip if criteria is still a dict (not yet deserialized) # This can happen when Pydantic doesn't fully deserialize polymorphic types - if isinstance(criteria, dict): - return + if isinstance(criteria, dict): # type: ignore[unreachable] + return # type: ignore[unreachable] raise NotImplementedError("Subclasses must implement _check_criteria") diff --git a/circe/check/checkers/comparisons.py b/circe/check/checkers/comparisons.py index b8c44eb..29306aa 100644 --- a/circe/check/checkers/comparisons.py +++ b/circe/check/checkers/comparisons.py @@ -119,7 +119,7 @@ def compare_to(filter_val: "ObservationFilter", window: "Window") -> int: An integer representing the comparison result """ if filter_val is None or window is None: - return 0 + return 0 # type: ignore[unreachable] range1 = filter_val.post_days + filter_val.prior_days range2_start = 0 @@ -144,7 +144,7 @@ def is_before(window: "Window") -> bool: True if the window is before, False otherwise """ if window is None: - return False + return False # type: ignore[unreachable] return Comparisons.is_before_endpoint(window.start) and not Comparisons.is_after_endpoint(window.end) @staticmethod diff --git a/circe/check/checkers/death_time_window_check.py b/circe/check/checkers/death_time_window_check.py index 41f7d18..f65eb80 100644 --- a/circe/check/checkers/death_time_window_check.py +++ b/circe/check/checkers/death_time_window_check.py @@ -8,6 +8,8 @@ Reference: JAVA_CLASS_MAPPINGS.md for Java equivalents. """ +from typing import Any + from ..operations.operations import Operations from ..utils.criteria_name_helper import CriteriaNameHelper from ..warning_severity import WarningSeverity @@ -122,7 +124,7 @@ def _check_criteria( """ name = f"{group_name} {CriteriaNameHelper.get_criteria_name(criteria.criteria)}" - match_result = Operations.match(criteria.criteria) + match_result: Any = Operations.match(criteria.criteria) match_result.is_a(Death) match_result.then( lambda death: ( diff --git a/circe/check/checkers/drug_era_check.py b/circe/check/checkers/drug_era_check.py index 2d78f8d..ee2b42d 100644 --- a/circe/check/checkers/drug_era_check.py +++ b/circe/check/checkers/drug_era_check.py @@ -8,6 +8,8 @@ Reference: JAVA_CLASS_MAPPINGS.md for Java equivalents. """ +from typing import Any + from ..operations.operations import Operations from ..warning_severity import WarningSeverity from .base_corelated_criteria_check import BaseCorelatedCriteriaCheck @@ -53,15 +55,15 @@ def _check_criteria( reporter: The warning reporter to use """ # Handle case where criteria is still a dict (not yet deserialized) - if isinstance(criteria, dict): + if isinstance(criteria, dict): # type: ignore[unreachable] # Skip validation for dict-based criteria - they need to be deserialized first - return + return # type: ignore[unreachable] # Ensure criteria has a criteria attribute if not hasattr(criteria, "criteria") or not criteria.criteria: return - match_result = Operations.match(criteria.criteria) + match_result: Any = Operations.match(criteria.criteria) match_result.is_a(DrugEra) match_result.then( lambda c: ( diff --git a/circe/check/checkers/exit_criteria_check.py b/circe/check/checkers/exit_criteria_check.py index 5073d06..6385ee8 100644 --- a/circe/check/checkers/exit_criteria_check.py +++ b/circe/check/checkers/exit_criteria_check.py @@ -8,6 +8,8 @@ Reference: JAVA_CLASS_MAPPINGS.md for Java equivalents. """ +from typing import Any + from ..operations.operations import Operations from .base_check import BaseCheck from .warning_reporter import WarningReporter @@ -39,7 +41,7 @@ def _check(self, expression: "CohortExpression", reporter: WarningReporter) -> N expression: The cohort expression to check reporter: The warning reporter to use """ - match_result = Operations.match(expression.end_strategy) + match_result: Any = Operations.match(expression.end_strategy) match_result.is_a(CustomEraStrategy) match_result.then( lambda s: ( diff --git a/circe/check/checkers/exit_criteria_days_offset_check.py b/circe/check/checkers/exit_criteria_days_offset_check.py index b98d235..d9e8fe7 100644 --- a/circe/check/checkers/exit_criteria_days_offset_check.py +++ b/circe/check/checkers/exit_criteria_days_offset_check.py @@ -8,6 +8,8 @@ Reference: JAVA_CLASS_MAPPINGS.md for Java equivalents. """ +from typing import Any + from ..operations.operations import Operations from ..warning_severity import WarningSeverity from .base_check import BaseCheck @@ -48,7 +50,7 @@ def _check(self, expression: "CohortExpression", reporter: WarningReporter) -> N expression: The cohort expression to check reporter: The warning reporter to use """ - match_result = Operations.match(expression.end_strategy) + match_result: Any = Operations.match(expression.end_strategy) match_result.is_a(DateOffsetStrategy) match_result.then( lambda s: ( diff --git a/circe/check/checkers/initial_event_check.py b/circe/check/checkers/initial_event_check.py index 68fabcd..9564f92 100644 --- a/circe/check/checkers/initial_event_check.py +++ b/circe/check/checkers/initial_event_check.py @@ -8,6 +8,8 @@ Reference: JAVA_CLASS_MAPPINGS.md for Java equivalents. """ +from typing import Any + from ..operations.operations import Operations from .base_check import BaseCheck from .warning_reporter import WarningReporter @@ -37,7 +39,7 @@ def _check(self, expression: "CohortExpression", reporter: WarningReporter) -> N expression: The cohort expression to check reporter: The warning reporter to use """ - match_result = Operations.match(expression) + match_result: Any = Operations.match(expression) match_result.when( lambda e: ( e.primary_criteria is None diff --git a/circe/check/checkers/no_exit_criteria_check.py b/circe/check/checkers/no_exit_criteria_check.py index 0f88c38..bca47d4 100644 --- a/circe/check/checkers/no_exit_criteria_check.py +++ b/circe/check/checkers/no_exit_criteria_check.py @@ -8,6 +8,8 @@ Reference: JAVA_CLASS_MAPPINGS.md for Java equivalents. """ +from typing import Any + from ..operations.operations import Operations from ..warning_severity import WarningSeverity from .base_check import BaseCheck @@ -46,7 +48,7 @@ def _check(self, expression: "CohortExpression", reporter: WarningReporter) -> N expression: The cohort expression to check reporter: The warning reporter to use """ - match_result = Operations.match(expression) + match_result: Any = Operations.match(expression) match_result.when( lambda e: ( e.primary_criteria diff --git a/circe/check/checkers/ocurrence_check.py b/circe/check/checkers/ocurrence_check.py index 81e43b1..1ae8b1c 100644 --- a/circe/check/checkers/ocurrence_check.py +++ b/circe/check/checkers/ocurrence_check.py @@ -8,7 +8,7 @@ Reference: JAVA_CLASS_MAPPINGS.md for Java equivalents. """ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any from ..operations.operations import Operations from ..warning_severity import WarningSeverity @@ -52,6 +52,6 @@ def _check_criteria( reporter: The warning reporter to use """ if criteria.occurrence: - match_result = Operations.match(criteria.occurrence) + match_result: Any = Operations.match(criteria.occurrence) match_result.when(lambda o: o.type == self.AT_LEAST and o.count == 0) match_result.then(lambda o: reporter(self.AT_LEAST_0_WARNING)) diff --git a/circe/check/checkers/range_check.py b/circe/check/checkers/range_check.py index 30082b3..8bd6b16 100644 --- a/circe/check/checkers/range_check.py +++ b/circe/check/checkers/range_check.py @@ -69,8 +69,8 @@ def _check_inclusion_rules(self, expression: "CohortExpression", reporter: Warni if rule.expression and rule.expression.criteria_list: for criteria in rule.expression.criteria_list: # Handle both dict and CorelatedCriteria objects - if isinstance(criteria, dict): - start_window = criteria.get("startWindow") or criteria.get("start_window") + if isinstance(criteria, dict): # type: ignore[unreachable] + start_window = criteria.get("startWindow") or criteria.get("start_window") # type: ignore[unreachable] end_window = criteria.get("endWindow") or criteria.get("end_window") else: start_window = getattr(criteria, "start_window", None) or getattr( diff --git a/circe/check/checkers/range_checker_factory.py b/circe/check/checkers/range_checker_factory.py index 0d4702f..3c9f36c 100644 --- a/circe/check/checkers/range_checker_factory.py +++ b/circe/check/checkers/range_checker_factory.py @@ -8,7 +8,7 @@ Reference: JAVA_CLASS_MAPPINGS.md for Java equivalents. """ -from typing import Callable, Optional +from typing import Any, Callable, Optional from ..constants import Constants from ..operations.operations import Operations @@ -613,7 +613,7 @@ def warning(template: str) -> None: if isinstance(range_val, DateRange): # Date range checks - match_result = Operations.match(range_val) + match_result: Any = Operations.match(range_val) match_result.when(lambda r: r.value is not None and not Comparisons.is_date_valid(r.value)).then( lambda x: warning(self.WARNING_DATE_IS_INVALID) ) @@ -639,7 +639,7 @@ def warning(template: str) -> None: ) elif isinstance(range_val, NumericRange): # Numeric range checks - match_result = Operations.match(range_val) + match_result: Any = Operations.match(range_val) match_result.when(lambda r: r.op is not None and r.op.endswith("bt")).then( lambda r: ( Operations.match(r) @@ -673,7 +673,7 @@ def check_range(self, period: Optional["Period"], criteria_name: str, attribute: def warning(template: str) -> None: self._reporter(template, self._group_name, criteria_name, attribute) - match_result = Operations.match(period) + match_result: Any = Operations.match(period) match_result.when( lambda x: x.start_date is not None and not Comparisons.is_date_valid(x.start_date) ).then(lambda x: warning(self.WARNING_DATE_IS_INVALID)) diff --git a/circe/check/checkers/time_window_check.py b/circe/check/checkers/time_window_check.py index 8e2a222..410f34d 100644 --- a/circe/check/checkers/time_window_check.py +++ b/circe/check/checkers/time_window_check.py @@ -8,7 +8,7 @@ Reference: JAVA_CLASS_MAPPINGS.md for Java equivalents. """ -from typing import Optional +from typing import Any, Optional from ..operations.operations import Operations from ..utils.criteria_name_helper import CriteriaNameHelper @@ -77,7 +77,7 @@ def _check_criteria( """ name = f"{group_name} {CriteriaNameHelper.get_criteria_name(criteria.criteria)}" - match_result = Operations.match(criteria) + match_result: Any = Operations.match(criteria) match_result.when( lambda c: ( c.start_window is not None diff --git a/circe/check/utils/criteria_name_helper.py b/circe/check/utils/criteria_name_helper.py index fa3d484..dc083ef 100644 --- a/circe/check/utils/criteria_name_helper.py +++ b/circe/check/utils/criteria_name_helper.py @@ -90,5 +90,5 @@ def get_criteria_name(criteria) -> str: .is_a(PayerPlanPeriod) .then_return(lambda c: Constants.Criteria.PAYER_PLAN_PERIOD) .value() - or "unknown criteria" + or "unknown criteria" # type: ignore[unreachable] ) diff --git a/circe/cohortdefinition/builders/base.py b/circe/cohortdefinition/builders/base.py index d936ab9..3cf9c9f 100644 --- a/circe/cohortdefinition/builders/base.py +++ b/circe/cohortdefinition/builders/base.py @@ -64,8 +64,6 @@ def get_criteria_sql_with_options(self, criteria: T, options: Optional[BuilderOp ) else: query = query.replace("@additionalColumns", "") - else: - query = query.replace("@additionalColumns", "") return query diff --git a/circe/cohortdefinition/builders/visit_occurrence.py b/circe/cohortdefinition/builders/visit_occurrence.py index 5326373..b68ec3f 100644 --- a/circe/cohortdefinition/builders/visit_occurrence.py +++ b/circe/cohortdefinition/builders/visit_occurrence.py @@ -256,8 +256,6 @@ def resolve_where_clauses( return where_clauses - return where_clauses - def embed_ordinal_expression( self, query: str, diff --git a/circe/cohortdefinition/code_generator.py b/circe/cohortdefinition/code_generator.py index 718595c..44e64d5 100644 --- a/circe/cohortdefinition/code_generator.py +++ b/circe/cohortdefinition/code_generator.py @@ -106,7 +106,7 @@ def instance_is_pydantic(o): # Generate Imports import_lines = [] # Group by module - module_map = {} + module_map: dict[str, list[str]] = {} for cls in required_classes: mod = cls.__module__ if mod not in module_map: diff --git a/circe/cohortdefinition/cohort_expression_query_builder.py b/circe/cohortdefinition/cohort_expression_query_builder.py index a48be93..b275ce6 100644 --- a/circe/cohortdefinition/cohort_expression_query_builder.py +++ b/circe/cohortdefinition/cohort_expression_query_builder.py @@ -1471,9 +1471,9 @@ def get_criteria_sql(self, criteria: Criteria, options: Optional[BuilderOptions] Java equivalent: Various getCriteriaSql methods """ # Handle case where criteria is still a dict (shouldn't happen, but be defensive) - if isinstance(criteria, dict): + if isinstance(criteria, dict): # type: ignore[unreachable] # Try to deserialize it - import here to avoid circular dependency issues - from .criteria import ConditionEra as CE + from .criteria import ConditionEra as CE # type: ignore[unreachable] from .criteria import ConditionOccurrence as CO from .criteria import Death as D from .criteria import DeviceExposure as DevE diff --git a/circe/cohortdefinition/criteria.py b/circe/cohortdefinition/criteria.py index f8c5a78..d1542b1 100644 --- a/circe/cohortdefinition/criteria.py +++ b/circe/cohortdefinition/criteria.py @@ -271,14 +271,6 @@ def _serialize_polymorphic(self, serializer, info): return {self.__class__.__name__: data} - # Get the serialized data using default serialization - data = serializer(self) - # Wrap in class name for polymorphic deserialization in Java - # Only wrap if this is a subclass (not the base Criteria class) - if self.__class__.__name__ != "Criteria": - return {self.__class__.__name__: data} - return data - def accept(self, dispatcher: Any, options: Optional[Any] = None) -> str: """Accept method for visitor pattern.""" return dispatcher.get_criteria_sql(self, options) @@ -1146,7 +1138,7 @@ def deserialize_criteria_list(cls, v: Any) -> Any: # Helper window normalizer (same as before) def normalize_window(window_dict: dict) -> dict: if not isinstance(window_dict, dict): - return window_dict + return window_dict # type: ignore[unreachable] normalized = {} if "UseEventEnd" in window_dict: normalized["useEventEnd"] = window_dict["UseEventEnd"] diff --git a/circe/cohortdefinition/printfriendly/markdown_render.py b/circe/cohortdefinition/printfriendly/markdown_render.py index 459484c..e2a1c76 100644 --- a/circe/cohortdefinition/printfriendly/markdown_render.py +++ b/circe/cohortdefinition/printfriendly/markdown_render.py @@ -238,7 +238,7 @@ def _format_number(self, value: Union[int, float]) -> str: Formatted string (e.g. "1,500" or "1.5") """ if value is None: - return "" + return "" # type: ignore[unreachable] # If matches integer, convert to int for clean formatting if isinstance(value, float) and value.is_integer(): diff --git a/circe/execution/lower/condition_era.py b/circe/execution/lower/condition_era.py index afbdc1f..5786fa1 100644 --- a/circe/execution/lower/condition_era.py +++ b/circe/execution/lower/condition_era.py @@ -4,7 +4,7 @@ from circe.extensions import lowerer from ..normalize.criteria import NormalizedCriterion -from ..plan.events import EventPlan +from ..plan.events import EventPlan, PlanStep from ..plan.schema import OCCURRENCE_COUNT from .common import ( append_duration_filter, @@ -21,7 +21,7 @@ def lower_condition_era( criterion_index: int, ) -> EventPlan: steps = lower_common_steps(criterion) - post_standardize_steps = [] + post_standardize_steps: list[PlanStep] = [] raw = criterion.raw_criteria append_numeric_filter( diff --git a/circe/execution/lower/dose_era.py b/circe/execution/lower/dose_era.py index 8ae286a..53daaa3 100644 --- a/circe/execution/lower/dose_era.py +++ b/circe/execution/lower/dose_era.py @@ -4,7 +4,7 @@ from circe.extensions import lowerer from ..normalize.criteria import NormalizedCriterion -from ..plan.events import EventPlan +from ..plan.events import EventPlan, PlanStep from .common import append_duration_filter, build_standard_domain_plan, lower_common_steps @@ -15,7 +15,7 @@ def lower_dose_era( criterion_index: int, ) -> EventPlan: steps = lower_common_steps(criterion) - post_standardize_steps = [] + post_standardize_steps: list[PlanStep] = [] append_duration_filter(post_standardize_steps, value=criterion.raw_criteria.era_length) return build_standard_domain_plan( diff --git a/circe/execution/lower/drug_era.py b/circe/execution/lower/drug_era.py index 8d32221..9b388f0 100644 --- a/circe/execution/lower/drug_era.py +++ b/circe/execution/lower/drug_era.py @@ -4,7 +4,7 @@ from circe.extensions import lowerer from ..normalize.criteria import NormalizedCriterion -from ..plan.events import EventPlan +from ..plan.events import EventPlan, PlanStep from ..plan.schema import GAP_DAYS, OCCURRENCE_COUNT from .common import ( append_duration_filter, @@ -21,7 +21,7 @@ def lower_drug_era( criterion_index: int, ) -> EventPlan: steps = lower_common_steps(criterion) - post_standardize_steps = [] + post_standardize_steps: list[PlanStep] = [] raw = criterion.raw_criteria append_numeric_filter( diff --git a/circe/execution/normalize/cohort.py b/circe/execution/normalize/cohort.py index 5838fe6..b2f657f 100644 --- a/circe/execution/normalize/cohort.py +++ b/circe/execution/normalize/cohort.py @@ -82,7 +82,7 @@ def _extract_codesets(concept_sets: list[ConceptSet]) -> dict[int, NormalizedCon for concept_set in concept_sets or []: if concept_set is None or concept_set.id is None: - continue + continue # type: ignore[unreachable] set_id = int(concept_set.id) expression = concept_set.expression if not expression: @@ -102,7 +102,7 @@ def _extract_codesets(concept_sets: list[ConceptSet]) -> dict[int, NormalizedCon for item in expression.items or []: if item is None: - continue + continue # type: ignore[unreachable] if item.concept is None or item.concept.concept_id is None: continue items.append( diff --git a/circe/extensions/__init__.py b/circe/extensions/__init__.py index b91a94a..d67a624 100644 --- a/circe/extensions/__init__.py +++ b/circe/extensions/__init__.py @@ -32,10 +32,10 @@ class WaveformOccurrenceMarkdownRenderer: from typing import TYPE_CHECKING, Callable, Optional, Union if TYPE_CHECKING: - from .cohortdefinition.builders.base import CriteriaSqlBuilder - from .cohortdefinition.criteria import Criteria - from .execution.lower.criteria import LowerFn - from .execution.normalize.criteria import NormalizedCriterion + from ..cohortdefinition.builders.base import CriteriaSqlBuilder + from ..cohortdefinition.criteria import Criteria + from ..execution.lower.criteria import LowerFn + from ..execution.normalize.criteria import NormalizedCriterion NormalizerFn = Callable[["Criteria"], "NormalizedCriterion"] @@ -43,7 +43,7 @@ class WaveformOccurrenceMarkdownRenderer: class ExtensionRegistry: """Central registry for OMOP CDM extensions.""" - def __init__(self): + def __init__(self) -> None: # Maps criteria names to criteria classes (for JSON deserialization) self._criteria_classes: dict[str, type[Criteria]] = {} @@ -211,10 +211,10 @@ class WaveformOccurrence(Criteria): """ def decorator(cls: "type['Criteria']") -> "type['Criteria']": - _registry.register_criteria_class(name, cls) # type: ignore[arg-type] + _registry.register_criteria_class(name, cls) return cls - return decorator # type: ignore[return-value] + return decorator def sql_builder( @@ -233,10 +233,10 @@ class WaveformOccurrenceSqlBuilder(CriteriaSqlBuilder): """ def decorator(builder_cls: "type['CriteriaSqlBuilder']") -> "type['CriteriaSqlBuilder']": - _registry.register_sql_builder(criteria_cls, builder_cls) # type: ignore[arg-type] + _registry.register_sql_builder(criteria_cls, builder_cls) return builder_cls - return decorator # type: ignore[return-value] + return decorator def lowerer(criteria_cls: "type['Criteria']") -> Callable[["LowerFn"], "LowerFn"]: @@ -253,7 +253,7 @@ def lower_waveform_occurrence(criterion, *, criterion_index): """ def decorator(fn: "LowerFn") -> "LowerFn": - _registry.register_lowerer(criteria_cls, fn) # type: ignore[arg-type] + _registry.register_lowerer(criteria_cls, fn) return fn return decorator @@ -273,7 +273,7 @@ def normalize_waveform_occurrence(criteria): """ def decorator(fn: NormalizerFn) -> NormalizerFn: - _registry.register_normalizer(criteria_cls, fn) # type: ignore[arg-type] + _registry.register_normalizer(criteria_cls, fn) return fn return decorator @@ -295,7 +295,7 @@ class WaveformOccurrenceMarkdownRenderer: """ def decorator(cls: type) -> type: - _registry.register_markdown_template(criteria_cls, template_name) # type: ignore[arg-type] + _registry.register_markdown_template(criteria_cls, template_name) return cls return decorator diff --git a/circe/extensions/waveform/builders/waveform_channel_metadata.py b/circe/extensions/waveform/builders/waveform_channel_metadata.py index f5fe96e..eace3fd 100644 --- a/circe/extensions/waveform/builders/waveform_channel_metadata.py +++ b/circe/extensions/waveform/builders/waveform_channel_metadata.py @@ -43,7 +43,7 @@ def get_criteria_sql_with_options( query = self.get_query_template() where_clauses = [] - join_clauses = [] + join_clauses: list[str] = [] codeset_clause = "" # Link to registry file diff --git a/circe/extensions/waveform/builders/waveform_feature.py b/circe/extensions/waveform/builders/waveform_feature.py index 7ea90c7..006e7e8 100644 --- a/circe/extensions/waveform/builders/waveform_feature.py +++ b/circe/extensions/waveform/builders/waveform_feature.py @@ -49,7 +49,7 @@ def get_criteria_sql_with_options(self, criteria: WaveformFeature, options: Buil query = self.get_query_template() where_clauses = [] - join_clauses = [] + join_clauses: list[str] = [] codeset_clause = "" # Parent links diff --git a/circe/extensions/waveform/builders/waveform_occurrence.py b/circe/extensions/waveform/builders/waveform_occurrence.py index 04124f9..9f6cd12 100644 --- a/circe/extensions/waveform/builders/waveform_occurrence.py +++ b/circe/extensions/waveform/builders/waveform_occurrence.py @@ -52,7 +52,7 @@ def get_criteria_sql_with_options(self, criteria: WaveformOccurrence, options: B query = self.get_query_template() where_clauses = [] - join_clauses = [] + join_clauses: list[str] = [] codeset_clause = "" # Filter by waveform occurrence concept diff --git a/circe/extensions/waveform/builders/waveform_registry.py b/circe/extensions/waveform/builders/waveform_registry.py index e241fe0..f91b8b6 100644 --- a/circe/extensions/waveform/builders/waveform_registry.py +++ b/circe/extensions/waveform/builders/waveform_registry.py @@ -45,7 +45,7 @@ def get_criteria_sql_with_options(self, criteria: WaveformRegistry, options: Bui query = self.get_query_template() where_clauses = [] - join_clauses = [] + join_clauses: list[str] = [] codeset_clause = "" # Link to parent occurrence diff --git a/pyproject.toml b/pyproject.toml index 401d3dd..d78d6fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -110,6 +110,29 @@ warn_no_return = true warn_unreachable = true strict_equality = true +[[tool.mypy.overrides]] +module = "ibis.*" +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = ["litellm.*", "dotenv.*"] +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = ["circe.chat", "circe.prompt_builder"] +ignore_errors = true + +[[tool.mypy.overrides]] +module = [ + "circe.execution.ibis.*", + "circe.execution.engine.*", + "circe.execution.ibis_compat", + "circe.execution.databricks_compat", +] +disallow_untyped_defs = false +disallow_incomplete_defs = false +warn_return_any = false + [tool.coverage.run] source = ["circe"] omit = [