Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/ruff_benchmark/benches/ty_walltime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ static ALTAIR: Benchmark = Benchmark::new(
max_dep_date: TY_ECOSYSTEM_PIN,
python_version: SupportedPythonVersion::Py311,
},
3,
7,
);

static COLOUR_SCIENCE: Benchmark = Benchmark::new(
Expand Down Expand Up @@ -211,7 +211,7 @@ static TANJUN: Benchmark = Benchmark::new(
max_dep_date: TY_ECOSYSTEM_PIN,
python_version: SupportedPythonVersion::Py311,
},
110,
120,
);

static STATIC_FRAME: Benchmark = Benchmark::new(
Expand Down
37 changes: 37 additions & 0 deletions crates/ty/docs/configuration.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/ty/tests/cli/config_option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ fn cli_config_args_invalid_option() -> anyhow::Result<()> {
|
1 | bad-option=true
| ^^^^^^^^^^
unknown field `bad-option`, expected one of `environment`, `src`, `rules`, `terminal`, `analysis`, `overrides`
unknown field `bad-option`, expected one of `environment`, `src`, `rules`, `terminal`, `analysis`, `semantics`, `overrides`
Usage: ty <COMMAND>
Expand Down
38 changes: 12 additions & 26 deletions crates/ty_ide/src/inlay_hints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7133,33 +7133,20 @@ Source with applied edits:
def f(xyxy: object):
if isinstance(xyxy, list):
x[: Top[list[Unknown]]] = xyxy
x[: list[Unknown]] = xyxy
---------------------------------------------
info[inlay-hint-location]: Inlay Hint Target
--> stdlib/ty_extensions.pyi:LL:1
|
LL | Top: _SpecialForm
| ^^^
|
info: Source
--> main2.py:LL:13
|
LL | x[: Top[list[Unknown]]] = xyxy
| ^^^
|
info[inlay-hint-location]: Inlay Hint Target
--> stdlib/builtins.pyi:LL:7
|
LL | class list(MutableSequence[_T]):
| ^^^^
|
info: Source
--> main2.py:LL:17
--> main2.py:LL:13
|
LL | x[: Top[list[Unknown]]] = xyxy
| ^^^^
LL | x[: list[Unknown]] = xyxy
| ^^^^
|
info[inlay-hint-location]: Inlay Hint Target
Expand All @@ -7169,23 +7156,22 @@ Source with applied edits:
| ^^^^^^^
|
info: Source
--> main2.py:LL:22
--> main2.py:LL:18
|
LL | x[: Top[list[Unknown]]] = xyxy
| ^^^^^^^
LL | x[: list[Unknown]] = xyxy
| ^^^^^^^
|
---------------------------------------------
info[inlay-hint-edit]: Inlay hint edits
--> main.py:1:1
|
1 + from ty_extensions import Top
2 + from ty_extensions import Unknown
3 |
4 | def f(xyxy: object):
5 | if isinstance(xyxy, list):
1 + from ty_extensions import Unknown
2 |
3 | def f(xyxy: object):
4 | if isinstance(xyxy, list):
- x = xyxy
6 + x: Top[list[Unknown]] = xyxy
5 + x: list[Unknown] = xyxy
|
");
}
Expand Down
12 changes: 10 additions & 2 deletions crates/ty_project/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use ty_python_core::program::{
FallibleStrategy, MisconfigurationStrategy, Program, UseDefaultStrategy,
};
use ty_python_semantic::lint::{LintRegistry, RuleSelection};
use ty_python_semantic::{AnalysisSettings, Db as SemanticDb};
use ty_python_semantic::{AnalysisSettings, Db as SemanticDb, SemanticSettings};

mod changes;
mod ignore;
Expand Down Expand Up @@ -549,6 +549,10 @@ impl SemanticDb for ProjectDatabase {
settings.analysis(self)
}

fn semantic_settings(&self, _file: File) -> &SemanticSettings {
self.project().settings(self).semantics()
}

fn verbose(&self) -> bool {
self.project().verbose(self)
}
Expand Down Expand Up @@ -629,7 +633,7 @@ pub(crate) mod testing {
use ty_python_core::platform::PythonPlatform;
use ty_python_core::program::{FallibleStrategy, Program, ProgramSettings};
use ty_python_semantic::lint::{LintRegistry, RuleSelection};
use ty_python_semantic::{AnalysisSettings, PythonVersionWithSource};
use ty_python_semantic::{AnalysisSettings, PythonVersionWithSource, SemanticSettings};

use crate::db::Db;
use crate::{Project, ProjectMetadata};
Expand Down Expand Up @@ -778,6 +782,10 @@ pub(crate) mod testing {
self.project().settings(self).analysis()
}

fn semantic_settings(&self, _file: ruff_db::files::File) -> &SemanticSettings {
self.project().settings(self).semantics()
}

fn verbose(&self) -> bool {
false
}
Expand Down
68 changes: 65 additions & 3 deletions crates/ty_project/src/metadata/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ use ty_python_core::platform::PythonPlatform;
use ty_python_core::program::{MisconfigurationStrategy, ProgramSettings};
use ty_python_semantic::lint::{Level, LintSource, RuleSelection};
use ty_python_semantic::{
AnalysisSettings, PythonEnvironment, PythonVersionFileSource, PythonVersionSource,
PythonVersionWithSource, SitePackagesPaths, SysPrefixPathOrigin,
inferred_python_version_source_annotation,
AnalysisSettings, IsInstanceNarrowing, PythonEnvironment, PythonVersionFileSource,
PythonVersionSource, PythonVersionWithSource, SemanticSettings, SitePackagesPaths,
SysPrefixPathOrigin, inferred_python_version_source_annotation,
};
use ty_static::EnvVars;

Expand Down Expand Up @@ -100,6 +100,11 @@ pub struct Options {
#[option_group]
pub analysis: Option<AnalysisOptions>,

/// Configures semantic type inference behavior.
#[serde(skip_serializing_if = "Option::is_none")]
#[option_group]
pub semantics: Option<SemanticsOptions>,

/// Override configurations for specific file patterns.
///
/// Each override specifies include/exclude patterns and rule configurations
Expand Down Expand Up @@ -490,6 +495,8 @@ impl Options {
};
let analysis = strategy.fallback(analysis_result, |_| AnalysisSettings::default())?;

let semantics = self.semantics.or_default().to_settings();

let overrides = self
.to_overrides_settings(db, project_root, &mut diagnostics)
.map_err(|err| ToSettingsError {
Expand All @@ -504,6 +511,7 @@ impl Options {
terminal,
src,
analysis,
semantics,
overrides,
};

Expand Down Expand Up @@ -1583,6 +1591,60 @@ impl AnalysisOptions {
}
}

#[derive(
Debug,
Default,
Clone,
Eq,
PartialEq,
Serialize,
Deserialize,
OptionsMetadata,
get_size2::GetSize,
)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct SemanticsOptions {
/// Controls how ty narrows to unspecialized generic classes in `isinstance()` checks.
///
/// With `strict`, ty narrows to the top materialization of the class. For example,
/// `isinstance(value, list)` narrows an `object` value to `Top[list[Unknown]]`, representing
/// a list with any possible specialization.
///
/// With `relaxed`, ty narrows to the class's default specialization instead. The same check
/// narrows an `object` value to `list[Unknown]`.
///
/// Defaults to `relaxed`.
#[option(
default = "relaxed",
value_type = "strict | relaxed",
example = r#"
isinstance-narrowing = "strict"
"#
)]
pub isinstance_narrowing: Option<RangedValue<IsInstanceNarrowing>>,
}

impl SemanticsOptions {
fn to_settings(&self) -> SemanticSettings {
SemanticSettings {
isinstance_narrowing: self
.isinstance_narrowing
.as_deref()
.copied()
.unwrap_or_default(),
}
}
}

impl Combine for SemanticsOptions {
fn combine_with(&mut self, other: Self) {
if self.isinstance_narrowing.is_none() {
self.isinstance_narrowing = other.isinstance_narrowing;
}
}
}

fn build_module_glob_set(
db: &dyn Db,
patterns: &[RangedValue<String>],
Expand Down
7 changes: 6 additions & 1 deletion crates/ty_project/src/metadata/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use std::sync::Arc;

use ruff_db::files::File;
use ty_combine::Combine;
use ty_python_semantic::AnalysisSettings;
use ty_python_semantic::lint::RuleSelection;
use ty_python_semantic::{AnalysisSettings, SemanticSettings};

use crate::metadata::options::{InnerOverrideOptions, OutputFormat};
use crate::{Db, glob::IncludeExcludeFilter};
Expand All @@ -27,6 +27,7 @@ pub struct Settings {
pub(super) terminal: TerminalSettings,
pub(super) src: SrcSettings,
pub(super) analysis: AnalysisSettings,
pub(super) semantics: SemanticSettings,

/// Settings for configuration overrides that apply to specific file patterns.
///
Expand Down Expand Up @@ -60,6 +61,10 @@ impl Settings {
pub fn analysis(&self) -> &AnalysisSettings {
&self.analysis
}

pub fn semantics(&self) -> &SemanticSettings {
&self.semantics
}
}

#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,9 @@ def match_exhaustive_generic[T](obj: GenericClass[T]) -> GenericClass[T]:
```toml
[environment]
python-version = "3.12"

[semantics]
isinstance-narrowing = "strict"
```

```py
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
```toml
[environment]
python-version = "3.13"

[semantics]
isinstance-narrowing = "strict"
```

[PEP 695] and Python 3.12 introduced new, more ergonomic syntax for type variables.
Expand Down
Loading
Loading