Compare commits
3 Commits
gankra/stm
...
david/defa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4833614c2 | ||
|
|
508c0a0861 | ||
|
|
0d2792517d |
7
.github/renovate.json5
vendored
7
.github/renovate.json5
vendored
@@ -2,12 +2,11 @@
|
||||
$schema: "https://docs.renovatebot.com/renovate-schema.json",
|
||||
dependencyDashboard: true,
|
||||
suppressNotifications: ["prEditedNotification"],
|
||||
extends: ["config:recommended"],
|
||||
extends: ["github>astral-sh/renovate-config"],
|
||||
labels: ["internal"],
|
||||
schedule: ["before 4am on Monday"],
|
||||
semanticCommits: "disabled",
|
||||
separateMajorMinor: false,
|
||||
prHourlyLimit: 10,
|
||||
enabledManagers: ["github-actions", "pre-commit", "cargo", "pep621", "pip_requirements", "npm"],
|
||||
cargo: {
|
||||
// See https://docs.renovatebot.com/configuration-options/#rangestrategy
|
||||
@@ -16,7 +15,7 @@
|
||||
pep621: {
|
||||
// The default for this package manager is to only search for `pyproject.toml` files
|
||||
// found at the repository root: https://docs.renovatebot.com/modules/manager/pep621/#file-matching
|
||||
fileMatch: ["^(python|scripts)/.*pyproject\\.toml$"],
|
||||
managerFilePatterns: ["^(python|scripts)/.*pyproject\\.toml$"],
|
||||
},
|
||||
pip_requirements: {
|
||||
// The default for this package manager is to run on all requirements.txt files:
|
||||
@@ -34,7 +33,7 @@
|
||||
npm: {
|
||||
// The default for this package manager is to only search for `package.json` files
|
||||
// found at the repository root: https://docs.renovatebot.com/modules/manager/npm/#file-matching
|
||||
fileMatch: ["^playground/.*package\\.json$"],
|
||||
managerFilePatterns: ["^playground/.*package\\.json$"],
|
||||
},
|
||||
"pre-commit": {
|
||||
enabled: true,
|
||||
|
||||
@@ -232,6 +232,32 @@ class C:
|
||||
reveal_type(not_a_method) # revealed: def not_a_method(self) -> Unknown
|
||||
```
|
||||
|
||||
## Different occurrences of `Self` represent different types
|
||||
|
||||
Here, both `Foo.foo` and `Bar.bar` use `Self`. When accessing a bound method, we replace any
|
||||
occurrences of `Self` with the bound `self` type. In this example, when we access `x.foo`, we only
|
||||
want to substitute the occurrences of `Self` in `Foo.foo` — that is, occurrences of `Self@foo`. The
|
||||
fact that `x` is an instance of `Foo[Self@bar]` (a completely different `Self` type) should not
|
||||
affect that subtitution. If we blindly substitute all occurrences of `Self`, we would get
|
||||
`Foo[Self@bar]` as the return type of the bound method.
|
||||
|
||||
```py
|
||||
from typing import Self
|
||||
|
||||
class Foo[T]:
|
||||
def foo(self: Self) -> T:
|
||||
raise NotImplementedError
|
||||
|
||||
class Bar:
|
||||
def bar(self: Self, x: Foo[Self]):
|
||||
# revealed: bound method Foo[Self@bar].foo() -> Self@bar
|
||||
reveal_type(x.foo)
|
||||
|
||||
def f[U: Bar](x: Foo[U]):
|
||||
# revealed: bound method Foo[U@f].foo() -> U@f
|
||||
reveal_type(x.foo)
|
||||
```
|
||||
|
||||
## typing_extensions
|
||||
|
||||
```toml
|
||||
|
||||
@@ -190,14 +190,10 @@ def _(
|
||||
reveal_type(type_of_str_or_int) # revealed: type[str] | int
|
||||
reveal_type(int_or_callable) # revealed: int | ((str, /) -> bytes)
|
||||
reveal_type(callable_or_int) # revealed: ((str, /) -> bytes) | int
|
||||
# TODO should be Unknown | int
|
||||
reveal_type(type_var_or_int) # revealed: T@TypeVarOrInt | int
|
||||
# TODO should be int | Unknown
|
||||
reveal_type(int_or_type_var) # revealed: int | T@IntOrTypeVar
|
||||
# TODO should be Unknown | None
|
||||
reveal_type(type_var_or_none) # revealed: T@TypeVarOrNone | None
|
||||
# TODO should be None | Unknown
|
||||
reveal_type(none_or_type_var) # revealed: None | T@NoneOrTypeVar
|
||||
reveal_type(type_var_or_int) # revealed: Unknown | int
|
||||
reveal_type(int_or_type_var) # revealed: int | Unknown
|
||||
reveal_type(type_var_or_none) # revealed: Unknown | None
|
||||
reveal_type(none_or_type_var) # revealed: None | Unknown
|
||||
```
|
||||
|
||||
If a type is unioned with itself in a value expression, the result is just that type. No
|
||||
@@ -529,28 +525,18 @@ def _(
|
||||
annotated_unknown: AnnotatedType,
|
||||
optional_unknown: MyOptional,
|
||||
):
|
||||
# TODO: This should be `list[Unknown]`
|
||||
reveal_type(list_unknown) # revealed: list[T@MyList]
|
||||
# TODO: This should be `dict[Unknown, Unknown]`
|
||||
reveal_type(dict_unknown) # revealed: dict[T@MyDict, U@MyDict]
|
||||
# TODO: Should be `type[Unknown]`
|
||||
reveal_type(subclass_of_unknown) # revealed: type[T@MyType]
|
||||
# TODO: Should be `tuple[int, Unknown]`
|
||||
reveal_type(int_and_unknown) # revealed: tuple[int, T@IntAndType]
|
||||
# TODO: Should be `tuple[Unknown, Unknown]`
|
||||
reveal_type(pair_of_unknown) # revealed: tuple[T@Pair, T@Pair]
|
||||
# TODO: Should be `tuple[Unknown, Unknown]`
|
||||
reveal_type(unknown_and_unknown) # revealed: tuple[T@Sum, U@Sum]
|
||||
# TODO: Should be `list[Unknown] | tuple[Unknown, ...]`
|
||||
reveal_type(list_or_tuple) # revealed: list[T@ListOrTuple] | tuple[T@ListOrTuple, ...]
|
||||
# TODO: Should be `list[Unknown] | tuple[Unknown, ...]`
|
||||
reveal_type(list_or_tuple_legacy) # revealed: list[T@ListOrTupleLegacy] | tuple[T@ListOrTupleLegacy, ...]
|
||||
reveal_type(list_unknown) # revealed: list[Unknown]
|
||||
reveal_type(dict_unknown) # revealed: dict[Unknown, Unknown]
|
||||
reveal_type(subclass_of_unknown) # revealed: type[Unknown]
|
||||
reveal_type(int_and_unknown) # revealed: tuple[int, Unknown]
|
||||
reveal_type(pair_of_unknown) # revealed: tuple[Unknown, Unknown]
|
||||
reveal_type(unknown_and_unknown) # revealed: tuple[Unknown, Unknown]
|
||||
reveal_type(list_or_tuple) # revealed: list[Unknown] | tuple[Unknown, ...]
|
||||
reveal_type(list_or_tuple_legacy) # revealed: list[Unknown] | tuple[Unknown, ...]
|
||||
# TODO: Should be `(...) -> Unknown`
|
||||
reveal_type(my_callable) # revealed: @Todo(Callable[..] specialized with ParamSpec)
|
||||
# TODO: Should be `Unknown`
|
||||
reveal_type(annotated_unknown) # revealed: T@AnnotatedType
|
||||
# TODO: Should be `Unknown | None`
|
||||
reveal_type(optional_unknown) # revealed: T@MyOptional | None
|
||||
reveal_type(annotated_unknown) # revealed: Unknown
|
||||
reveal_type(optional_unknown) # revealed: Unknown | None
|
||||
```
|
||||
|
||||
For a type variable with a default, we use the default type:
|
||||
@@ -565,8 +551,7 @@ def _(
|
||||
list_of_int: MyListWithDefault,
|
||||
):
|
||||
reveal_type(list_of_str) # revealed: list[str]
|
||||
# TODO: this should be `list[int]`
|
||||
reveal_type(list_of_int) # revealed: list[T_default@MyListWithDefault]
|
||||
reveal_type(list_of_int) # revealed: list[int]
|
||||
```
|
||||
|
||||
(Generic) implicit type aliases can be used as base classes:
|
||||
|
||||
@@ -7540,6 +7540,15 @@ impl<'db> Type<'db> {
|
||||
self.apply_type_mapping_impl(db, type_mapping, tcx, &ApplyTypeMappingVisitor::default())
|
||||
}
|
||||
|
||||
/// Erase all free type variables in this type, replacing them with their defaults
|
||||
/// or `Unknown` if no default exists.
|
||||
///
|
||||
/// This is used when an implicit type alias containing free type variables is used
|
||||
/// in a type expression without explicit type arguments.
|
||||
pub(crate) fn erase_free_typevars(self, db: &'db dyn Db) -> Type<'db> {
|
||||
self.apply_type_mapping(db, &TypeMapping::EraseTypevars, TypeContext::default())
|
||||
}
|
||||
|
||||
fn apply_type_mapping_impl<'a>(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
@@ -7550,10 +7559,9 @@ impl<'db> Type<'db> {
|
||||
// If we are binding `typing.Self`, and this type is what we are binding `Self` to, return
|
||||
// early. This is not just an optimization, it also prevents us from infinitely expanding
|
||||
// the type, if it's something that can contain a `Self` reference.
|
||||
if let TypeMapping::BindSelf(self_type) = type_mapping
|
||||
&& self == *self_type
|
||||
{
|
||||
return self;
|
||||
match type_mapping {
|
||||
TypeMapping::BindSelf { self_type, .. } if self == *self_type => return self,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match self {
|
||||
@@ -7568,11 +7576,12 @@ impl<'db> Type<'db> {
|
||||
TypeMapping::Specialization(_) |
|
||||
TypeMapping::PartialSpecialization(_) |
|
||||
TypeMapping::PromoteLiterals(_) |
|
||||
TypeMapping::BindSelf(_) |
|
||||
TypeMapping::BindSelf { .. } |
|
||||
TypeMapping::ReplaceSelf { .. } |
|
||||
TypeMapping::Materialize(_) |
|
||||
TypeMapping::ReplaceParameterDefaults |
|
||||
TypeMapping::EagerExpansion => self,
|
||||
TypeMapping::EagerExpansion |
|
||||
TypeMapping::EraseTypevars => self,
|
||||
}
|
||||
}
|
||||
KnownInstanceType::UnionType(instance) => {
|
||||
@@ -7744,11 +7753,12 @@ impl<'db> Type<'db> {
|
||||
TypeMapping::Specialization(_) |
|
||||
TypeMapping::PartialSpecialization(_) |
|
||||
TypeMapping::BindLegacyTypevars(_) |
|
||||
TypeMapping::BindSelf(_) |
|
||||
TypeMapping::BindSelf { .. } |
|
||||
TypeMapping::ReplaceSelf { .. } |
|
||||
TypeMapping::Materialize(_) |
|
||||
TypeMapping::ReplaceParameterDefaults |
|
||||
TypeMapping::EagerExpansion |
|
||||
TypeMapping::EraseTypevars |
|
||||
TypeMapping::PromoteLiterals(PromoteLiteralsMode::Off) => self,
|
||||
TypeMapping::PromoteLiterals(PromoteLiteralsMode::On) => self.promote_literals_impl(db, tcx)
|
||||
}
|
||||
@@ -7757,11 +7767,12 @@ impl<'db> Type<'db> {
|
||||
TypeMapping::Specialization(_) |
|
||||
TypeMapping::PartialSpecialization(_) |
|
||||
TypeMapping::BindLegacyTypevars(_) |
|
||||
TypeMapping::BindSelf(_) |
|
||||
TypeMapping::BindSelf { .. } |
|
||||
TypeMapping::ReplaceSelf { .. } |
|
||||
TypeMapping::PromoteLiterals(_) |
|
||||
TypeMapping::ReplaceParameterDefaults |
|
||||
TypeMapping::EagerExpansion => self,
|
||||
TypeMapping::EagerExpansion |
|
||||
TypeMapping::EraseTypevars => self,
|
||||
TypeMapping::Materialize(materialization_kind) => match materialization_kind {
|
||||
MaterializationKind::Top => Type::object(),
|
||||
MaterializationKind::Bottom => Type::Never,
|
||||
@@ -8421,7 +8432,10 @@ pub enum TypeMapping<'a, 'db> {
|
||||
/// being used in.
|
||||
BindLegacyTypevars(BindingContext<'db>),
|
||||
/// Binds any `typing.Self` typevar with a particular `self` class.
|
||||
BindSelf(Type<'db>),
|
||||
BindSelf {
|
||||
self_type: Type<'db>,
|
||||
binding_context: Option<BindingContext<'db>>,
|
||||
},
|
||||
/// Replaces occurrences of `typing.Self` with a new `Self` type variable with the given upper bound.
|
||||
ReplaceSelf { new_upper_bound: Type<'db> },
|
||||
/// Create the top or bottom materialization of a type.
|
||||
@@ -8432,6 +8446,9 @@ pub enum TypeMapping<'a, 'db> {
|
||||
/// Apply eager expansion to the type.
|
||||
/// In the case of recursive type aliases, this will diverge, so that part will be replaced with `Divergent`.
|
||||
EagerExpansion,
|
||||
/// Replace all type variables with `Unknown`. This is used when an implicit type alias containing
|
||||
/// free type variables is used in a type expression without explicit type arguments.
|
||||
EraseTypevars,
|
||||
}
|
||||
|
||||
impl<'db> TypeMapping<'_, 'db> {
|
||||
@@ -8448,8 +8465,9 @@ impl<'db> TypeMapping<'_, 'db> {
|
||||
| TypeMapping::BindLegacyTypevars(_)
|
||||
| TypeMapping::Materialize(_)
|
||||
| TypeMapping::ReplaceParameterDefaults
|
||||
| TypeMapping::EagerExpansion => context,
|
||||
TypeMapping::BindSelf(_) => GenericContext::from_typevar_instances(
|
||||
| TypeMapping::EagerExpansion
|
||||
| TypeMapping::EraseTypevars => context,
|
||||
TypeMapping::BindSelf { .. } => GenericContext::from_typevar_instances(
|
||||
db,
|
||||
context
|
||||
.variables(db)
|
||||
@@ -8482,10 +8500,11 @@ impl<'db> TypeMapping<'_, 'db> {
|
||||
TypeMapping::Specialization(_)
|
||||
| TypeMapping::PartialSpecialization(_)
|
||||
| TypeMapping::BindLegacyTypevars(_)
|
||||
| TypeMapping::BindSelf(_)
|
||||
| TypeMapping::BindSelf { .. }
|
||||
| TypeMapping::ReplaceSelf { .. }
|
||||
| TypeMapping::ReplaceParameterDefaults
|
||||
| TypeMapping::EagerExpansion => self.clone(),
|
||||
| TypeMapping::EagerExpansion
|
||||
| TypeMapping::EraseTypevars => self.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9837,8 +9856,13 @@ impl<'db> BoundTypeVarInstance<'db> {
|
||||
TypeMapping::PartialSpecialization(partial) => {
|
||||
partial.get(db, self).unwrap_or(Type::TypeVar(self))
|
||||
}
|
||||
TypeMapping::BindSelf(self_type) => {
|
||||
if self.typevar(db).is_self(db) {
|
||||
TypeMapping::BindSelf {
|
||||
self_type,
|
||||
binding_context,
|
||||
} => {
|
||||
if self.typevar(db).is_self(db)
|
||||
&& binding_context.is_none_or(|context| self.binding_context(db) == context)
|
||||
{
|
||||
*self_type
|
||||
} else {
|
||||
Type::TypeVar(self)
|
||||
@@ -9859,6 +9883,12 @@ impl<'db> BoundTypeVarInstance<'db> {
|
||||
| TypeMapping::ReplaceParameterDefaults
|
||||
| TypeMapping::BindLegacyTypevars(_)
|
||||
| TypeMapping::EagerExpansion => Type::TypeVar(self),
|
||||
TypeMapping::EraseTypevars => {
|
||||
// Replace the type variable with its default, or Unknown if no default exists.
|
||||
self.default_type(db)
|
||||
.unwrap_or_else(Type::unknown)
|
||||
.apply_type_mapping(db, type_mapping, TypeContext::default())
|
||||
}
|
||||
TypeMapping::Materialize(materialization_kind) => {
|
||||
Type::TypeVar(self.materialize_impl(db, *materialization_kind, visitor))
|
||||
}
|
||||
|
||||
@@ -143,20 +143,27 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
TypeQualifiers::INIT_VAR,
|
||||
)
|
||||
}
|
||||
_ => TypeAndQualifiers::declared(
|
||||
ty.in_type_expression(
|
||||
builder.db(),
|
||||
builder.scope(),
|
||||
builder.typevar_binding_context,
|
||||
_ => {
|
||||
// When a name or attribute expression resolves to a type containing free
|
||||
// type variables (like a GenericAlias from an implicit type alias), we need
|
||||
// to erase them since the alias is being used without explicit type arguments.
|
||||
let erased = ty.erase_free_typevars(builder.db());
|
||||
TypeAndQualifiers::declared(
|
||||
erased
|
||||
.in_type_expression(
|
||||
builder.db(),
|
||||
builder.scope(),
|
||||
builder.typevar_binding_context,
|
||||
)
|
||||
.unwrap_or_else(|error| {
|
||||
error.into_fallback_type(
|
||||
&builder.context,
|
||||
annotation,
|
||||
builder.is_reachable(annotation),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.unwrap_or_else(|error| {
|
||||
error.into_fallback_type(
|
||||
&builder.context,
|
||||
annotation,
|
||||
builder.is_reachable(annotation),
|
||||
)
|
||||
}),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,16 +89,22 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
// https://typing.python.org/en/latest/spec/annotations.html#grammar-token-expression-grammar-type_expression
|
||||
match expression {
|
||||
ast::Expr::Name(name) => match name.ctx {
|
||||
ast::ExprContext::Load => self
|
||||
.infer_name_expression(name)
|
||||
.in_type_expression(self.db(), self.scope(), self.typevar_binding_context)
|
||||
.unwrap_or_else(|error| {
|
||||
error.into_fallback_type(
|
||||
&self.context,
|
||||
expression,
|
||||
self.is_reachable(expression),
|
||||
)
|
||||
}),
|
||||
ast::ExprContext::Load => {
|
||||
// When a name expression resolves to a type containing free type variables
|
||||
// (like a GenericAlias from an implicit type alias), we need to erase them
|
||||
// since the alias is being used without explicit type arguments.
|
||||
let ty = self.infer_name_expression(name);
|
||||
let erased = ty.erase_free_typevars(self.db());
|
||||
erased
|
||||
.in_type_expression(self.db(), self.scope(), self.typevar_binding_context)
|
||||
.unwrap_or_else(|error| {
|
||||
error.into_fallback_type(
|
||||
&self.context,
|
||||
expression,
|
||||
self.is_reachable(expression),
|
||||
)
|
||||
})
|
||||
}
|
||||
ast::ExprContext::Invalid => Type::unknown(),
|
||||
ast::ExprContext::Store | ast::ExprContext::Del => {
|
||||
todo_type!("Name expression annotation in Store/Del context")
|
||||
@@ -106,16 +112,20 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
},
|
||||
|
||||
ast::Expr::Attribute(attribute_expression) => match attribute_expression.ctx {
|
||||
ast::ExprContext::Load => self
|
||||
.infer_attribute_expression(attribute_expression)
|
||||
.in_type_expression(self.db(), self.scope(), self.typevar_binding_context)
|
||||
.unwrap_or_else(|error| {
|
||||
error.into_fallback_type(
|
||||
&self.context,
|
||||
expression,
|
||||
self.is_reachable(expression),
|
||||
)
|
||||
}),
|
||||
ast::ExprContext::Load => {
|
||||
// Same as name expressions - erase free type variables in attribute access
|
||||
let ty = self.infer_attribute_expression(attribute_expression);
|
||||
let erased = ty.erase_free_typevars(self.db());
|
||||
erased
|
||||
.in_type_expression(self.db(), self.scope(), self.typevar_binding_context)
|
||||
.unwrap_or_else(|error| {
|
||||
error.into_fallback_type(
|
||||
&self.context,
|
||||
expression,
|
||||
self.is_reachable(expression),
|
||||
)
|
||||
})
|
||||
}
|
||||
ast::ExprContext::Invalid => Type::unknown(),
|
||||
ast::ExprContext::Store | ast::ExprContext::Del => {
|
||||
todo_type!("Attribute expression annotation in Store/Del context")
|
||||
|
||||
@@ -29,9 +29,10 @@ use crate::types::generics::{
|
||||
};
|
||||
use crate::types::infer::nearest_enclosing_class;
|
||||
use crate::types::{
|
||||
ApplyTypeMappingVisitor, BoundTypeVarInstance, ClassLiteral, FindLegacyTypeVarsVisitor,
|
||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownClass, MaterializationKind,
|
||||
NormalizedVisitor, TypeContext, TypeMapping, TypeRelation, VarianceInferable, todo_type,
|
||||
ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, ClassLiteral,
|
||||
FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor,
|
||||
KnownClass, MaterializationKind, NormalizedVisitor, TypeContext, TypeMapping, TypeRelation,
|
||||
VarianceInferable, todo_type,
|
||||
};
|
||||
use crate::{Db, FxOrderSet};
|
||||
use ruff_python_ast::{self as ast, name::Name};
|
||||
@@ -667,19 +668,18 @@ impl<'db> Signature<'db> {
|
||||
let mut parameters = Parameters::new(db, parameters);
|
||||
let mut return_ty = self.return_ty;
|
||||
if let Some(self_type) = self_type {
|
||||
let self_mapping = TypeMapping::BindSelf {
|
||||
self_type,
|
||||
binding_context: self.definition.map(BindingContext::Definition),
|
||||
};
|
||||
parameters = parameters.apply_type_mapping_impl(
|
||||
db,
|
||||
&TypeMapping::BindSelf(self_type),
|
||||
&self_mapping,
|
||||
TypeContext::default(),
|
||||
&ApplyTypeMappingVisitor::default(),
|
||||
);
|
||||
return_ty = return_ty.map(|ty| {
|
||||
ty.apply_type_mapping(
|
||||
db,
|
||||
&TypeMapping::BindSelf(self_type),
|
||||
TypeContext::default(),
|
||||
)
|
||||
});
|
||||
return_ty = return_ty
|
||||
.map(|ty| ty.apply_type_mapping(db, &self_mapping, TypeContext::default()));
|
||||
}
|
||||
Self {
|
||||
generic_context: self.generic_context,
|
||||
@@ -690,19 +690,19 @@ impl<'db> Signature<'db> {
|
||||
}
|
||||
|
||||
pub(crate) fn apply_self(&self, db: &'db dyn Db, self_type: Type<'db>) -> Self {
|
||||
let self_mapping = TypeMapping::BindSelf {
|
||||
self_type,
|
||||
binding_context: self.definition.map(BindingContext::Definition),
|
||||
};
|
||||
let parameters = self.parameters.apply_type_mapping_impl(
|
||||
db,
|
||||
&TypeMapping::BindSelf(self_type),
|
||||
&self_mapping,
|
||||
TypeContext::default(),
|
||||
&ApplyTypeMappingVisitor::default(),
|
||||
);
|
||||
let return_ty = self.return_ty.map(|ty| {
|
||||
ty.apply_type_mapping(
|
||||
db,
|
||||
&TypeMapping::BindSelf(self_type),
|
||||
TypeContext::default(),
|
||||
)
|
||||
});
|
||||
let return_ty = self
|
||||
.return_ty
|
||||
.map(|ty| ty.apply_type_mapping(db, &self_mapping, TypeContext::default()));
|
||||
Self {
|
||||
generic_context: self.generic_context,
|
||||
definition: self.definition,
|
||||
|
||||
Reference in New Issue
Block a user