Intersections with Callable, in particular Any & Callable

In this issue, we shall discuss intersections with Callable. There is an unresolved issue with respect to Any & Callable, that stems from the following 2 axioms:

Intersections with Callable are resolved by intersecting with an equivalent Callback-Protocol (see current draft)
(x & y).foo = x.foo & y.foo  (see Eric's outline: https://github.com/CarliJoy/intersection_examples/issues/1#issuecomment-1657265867)

Both of these together can lead to infinite regress.
Therefore, we cannot use ① when intersecting function-types with function-types. One can distinguish 3 cases:

Intersections of function-types with non-function-types: then we can apply rule ①
Intersections of two function-types: regular subtyping and the more complicated function signature subtyping rules apply.
Intersections of function-types with Any

EDIT: These suggestions are outdated. The intersection with Any is irreducible. What needs to be discussed is what happens when the type is attempted to be evaluated with arguments args.

As I see it, there are 2 options of how to resolve ③:
Ⓐ For a strict interpretation of Any as an unknown type, Any could feasibly be Callable[..., R], which is a "consistent" subtype of Callable[[T1, T2, ..., Tn], R]. It follows that:
Any & Callable[[T1, T2, ..., Tn], R] = Callable[..., R]

The consequence is that (Any & T).foo(*args, **kwargs), is valid for any method foo of T, which as expressed in the Any intersection thread causes some pains, as it makes Any & T behave very similar to Any itself (only attribute types and function return types are preserved)
Ⓑ An alternative approach would hinge on a slight re-interpretation of Any in the context of intersections. One could demand that Any shall be interpreted as a disjoint type from whatever it is intersected with (= maximum compatibility). That is, in Any & T, we interpret Any as an unknown type that presumably shares no overlapping methods with T. This leads to
Any & Callable[[T1, T2, ..., Tn], R] = Callable[[T1, T2, ..., Tn], R]

I.e. we come back full circle to question if Any can be simplified away from intersections. Under this premise, we would have 3 simple rules, as I mentioned in https://github.com/CarliJoy/intersection_examples/issues/1#issuecomment-1657096910

Any & T is an irreducible form
(Any & T).foo ≅ T.foo if T implements foo
(Any & T).bar ≅ Any if T does not implement bar

So, does it make sense to have a general exemption to the rules https://github.com/CarliJoy/intersection_examples/issues/1#issuecomment-1657265867 when intersecting Any with function-types?

EDIT: There is possibly also a sort of middle ground option:
Ⓒ Only subtyping rules of Callable apply, but not the "consistent" subtyping rules. (see draft document for the distinction). That is, Any & Callable can mess with the argument types, but not the signature.
Any & Callable[[T1, T2, ..., Tn], R] = Callable[[Any, Any, ..., Any], R]

Under this, (Any & T).foo would have the same function signature as T.foo, but any argument type would be allowed.

If all of these are accurate:

That type checkers are willing to provide an optional warning for Any as an intersection operand
That there are no shortcuts that allow skipping certain checks that are not able to be shown as formally equivalent.
That these are the distinct places in this example that the intersection is consulted and how

x: A & B = ... # Here, we have to still check that x is both A and B

def f(x: A & B):
    x.foo()  # here, we have to check that this is consistent use with definitions for all existing defintions of foo in A & B

f(something)  # here we check that something is both A and B

No special case needed.

Wait a second, I think this actually means that this whole example from earlier should already work fine, we don't even need

That type checkers are willing to provide an optional warning for Any as an intersection operand

from where import UntypedLibraryType
from typing import Protocol

class Mine(Protocol):
    def foo(self, x: int, /) -> int: ...

def fn(x: UntypedLibraryType & Mine):
    x.foo(1)  # this is how I expect it to be used
    x.foo("blah")  # this works because UntypedLibraryType is Any, and I would intend this as an error

The type-checker will test whether the signature foo(__x: str) is compatible with Mine.foo and Any.foo, and will consequently fail. If this wasn't how it is working already, then it would apply in the same vein to
from typing import Protocol

class Mine(Protocol):
    def foo(self, x: int, /) -> int: ...

def fn(x: Mine):
    x.foo(1)
    x.foo("blah")

I think this really does mean that we can simplify Any & C = C for any function-type C.

The type-checker will test whether the signature foo(__x: str) is compatible with Mine.foo and Any.foo, and will consequently fail.

It need only be consistent with one of Mine.foo and Any.foo, and it is consistent with Any.foo.

@diabolo-dan why only with one of them?

Here's how type checkers handle calls on unions today in Python:
When A | B is called with args:

If either A or B is not callable with args, the operation is an error
If A and B are both callable with args, the return result is the union of the return results from A and B
Any is treated as Callable[..., Any]

By symmetry, I would think that the rule for intersections would be:
When A & B is called with args:

If neither A or B are callable with args, the operation is an error
If A is callable with args but B is not, the return result comes from A
If B is callable with args but A is not, the return result comes from B
If A and B are both callable with args, the return result is the intersection of the return results from A and B
Any is treated as Callable[..., Any]

And yes, this means that if an Any appears in either a union or an intersection, the resulting return type will include an Any. That's the nature of Any, and we should be consistent here.
Interestingly, TypeScript deviates from the above slightly in the case of intersections. If A and B are both callable with args, TypeScript uses the return type from A only. It apparently sorts the types (using an undocumented sorting algorithm) to maintain determinism and order independence of intersections. I verified this by swapping the names of A and B, and the evaluated return result swapped accordingly.
interface A {
    y: (val: string) => number;
    z: () => number;
}

interface B {
    y: (val: number) => string;
    z: () => string;
}

function f(c: A & B) {
    const x = c.x(); // Error
    const y1 = c.y(0); // string
    const y2 = c.y(''); // number
    const z = c.z(); // number (but becomes string if A and B interface names are swapped)
}

The behavior in TypeScript seems inconsistent to me, and the "undocumented sorting algorithm" means that it cannot be replicated from a spec unless that spec fully documents the way types should be sorted. I'd prefer not to avoid that for Python.

So, in principle, mypy could/would/should trigger the warn_return_any flag when (Any & Mine).foo("abc") is called, but not for (Any & Mine).foo(123) in the example above.

If A and B are both callable with args, the return result is the intersection of the return results from A and B

Is this clear? What about:
class A:
    def foo(x: int) -> int: ...
class B:
    def foo(x: str) -> str: ...
class C(A, B):
    @overload
    def foo(x: int) -> int: ...
    @overload
    def foo(x: str) -> str: ...

Under a sensible definition of intersection we should have C ≤ A & B, but the return type of C.foo is not int & str, it's an either or. I think this is just the rule 1.1 from https://github.com/CarliJoy/intersection_examples/issues/2#issuecomment-1657209326, that is Callable[..., T&S] ≤ Callable[..., T] & Callable[..., S]  but equality if and only if S≤T or T≤S.

Hm, I think there is a problematic case though, which is incompatible signatures:
class A:
    def foo(self, *, key: str) -> T: ... # takes 1 mandatory keyword argument "key"
class B:
    def foo(self) -> S

The issue here is that by the LSP, one is not allowed to have a subclass of B where foo has mandatory key arguments.
One is generally only allowed to add arguments with defaults and *args, and **kwargs.
I think one has to amend the rules as follows: When A & B is called with args:

If neither A or B are callable with args, the operation is an error
If A is callable with args, and signature(args) ≤ signature(B), the result comes from A.
If A is callable with args, but signature(args) incompatible with  signature(B), the operation is an error
If B is callable with args, and signature(args) ≤ signature(A), the result comes from B.
If B is callable with args, but signature(args) incompatible with  signature(A), the  operation is an error
If A and B are both callable with args, the return result is the intersection of the return results from A and B
Any is treated as Callable[..., Any]

Also, I wonder if "the operation is an error" can generally just be replaced by returning Never and type-checker raising whenever an instance of Never is created.

@erictraut I deleted the earlier comment, the argument was flawed...

@erictraut Sorry, but I have to retract my retraction, int & str is not a valid argument by contra-variance. Sorry for causing so much confusion...

I think most of this is ruled out by the original assignability check.
I don't think it should be picking one of A or B as typescript does. If the two are incompatible, the two are incompatible. If they aren't, compatible use should be acceptable, but the assignability concern says it needs to be compatible with both. We aren't stating that there is a direct subclass with a specific MRO with intersections, and even if we were, replacement says such a subclass would have to be compatible this way too.
@DiscordLiz linked an accepted (rust) RFC in #14 that has a section detailing detecting overlapping implementations that feels relevant here

There's also my comment here
quick relevant part:

As an exhaustive pattern for example
(A & B).foo
if A provides foo and B doesn't, A.foo
If B provides foo and A doesn't, B.foo
if both A and B provide foo, A.foo & B.foo
if neither A nor B provide foo, Never

Other than this not checking for compatibility between operands at the time of use (consistent subtyping, we should assume it from assignability), and only needing to check that use is compatible with each operand individually, this should align with how rust does this, and align with a general expectation of "I specified being compatible with each of these"

Going from that minimal overlap calculation, here would be some of my expectations
() -> None & (optional: bool=False) -> None = (optional: bool=False) -> None
(int) -> int & (int) -> str = Never

But in the above, we could modify it only slightly and also have a solvable case:
(int) -> int & (str) -> str = (T) -> T, for T: int | str

we also have to consider names as part of the api here when discussing something being compatible with both
(not_pos_only_name: int) -> int  & (different_not_pos_only_name: int) -> int = Never

@DiscordLiz When we take the literal definition of meet (=Intersection) and join (=Union) from partially ordered sets, then the meet/intersection is by definition the greatest lower bound
Definition: X is a greatest lower bound if and only if:

X ≤ A and X ≤ B  ("X" is a lower bound to both A and B)
If Y≤A and Y≤B then Y≤X ("X" is greater than any other lower bound)

Lemma:

A greatest lower bound need not exist for given A and B (well, technically we can assign Never)
If it exists, it is unique, hence we call it the greatest lower bound and annotate it as A&B.

In the example you have given, therefore, A&B should be the most general subclass possible that is both compatible with A and B, so something like
class C(A,B):
    def foo(self, wait: bool = False) -> None: ...
    def bar(self, wait: bool = False) -> None: ...

which is a valid subclass of both A and B. Note that in the example I gave in https://github.com/CarliJoy/intersection_examples/issues/16#issuecomment-1658764730, such a shared subclass is impossible due to LSP violation.

@randolf-scholz yes, that's exactly what I was getting at with that example, but also pointing out that Typescript's approach of just picking one side would never work for that case. I didn't feel I needed to spell that out, especially in the presence of prior conversation and further examples.

Regarding overlap violation, I think this is really complex since it depends on function signature subtyping, which needs to take into account the 5 different argument kinds possible in python: POSITIONAL_ONLY, POSITIONAL_OR_KEYWORD, VAR_POSITIONAL KEYWORD_ONLY and VAR_KEYWORD.

Regarding overlap violation, I think this is really complex since it depends on function signature subtyping, which needs to take into account the 5 different argument kinds possible in python: POSITIONAL_ONLY, POSITIONAL_OR_KEYWORD, VAR_POSITIONAL KEYWORD_ONLY and VAR_KEYWORD.

I don't think this makes it more complex, at least not much.
We can say that

Nothing should move fromPOSITIONAL_ONLY to POSITIONAL_OR_KEYWORD, as this would defeat the purpose of POSITIONAL_ONLY's existence, to not expose a name as part of the api
For symmetry with 1, nothing should move from  KEYWORD_ONLY to POSITIONAL_OR_KEYWORD *
Nothing can move from POSITIONAL_OR_KEYWORD to KEYWORD_ONLY or POSITIONAL_ONLY without breaking compatible use.
exposed parameter names must be unique across all groups in total
unexposed parameter names (POSITIONAL_ONLY) may be changed to allow compatibility with the prior point

I think we should just say that for the purposes of this, we handle each group individually, then check on handling points 4 & 5. While point 2 is stylistic in choice, rather than having a direct functional reason, I'd also rather not change library author's intent by intersecting their type with someone else's.
Additionally, if the same name shows up in different groups across callabled, it might be a stronger indication that they aren't used the same way and shouldn't be treated as compatible.
Not allowing movement between groups (1-3) allows a simple solution that does not need to consider these groups, but instead operate on each of them independently and check that the total signature is valid at the end.

So, does it make sense to have a general exemption to the rules #1 (comment) when intersecting Any with function-types?

I don't think it's a good idea to have an exception to the rule.  I think we should have a principle that changing any annotation from SomeClass to Any should not create type errors.  If you had:
Any & Callable[[T1, T2, ..., Tn], R] = Callable[[T1, T2, ..., Tn], R]

then it could create errors even though substituting a type for Any would have no error.
It's the same for the middle ground option.
I think it must be that:
Any & Callable[[T1, T2, ..., Tn], R] = Callable[..., R]

I don't think it's a good idea to get into the weeds about "signature compatibility".  The rule for calling A & B should be based only on the validity and return values of A and B and no examination of parameter specifications.
The key idea is that just because A is not callable with the arguments, it doesn't mean that A's subtype isn't callable with the arguments.  But if neither A nor B are callable with the arguments, then the type checker should return an error for the same reason that it does when A alone is called with incorrect arguments (A alone doesn't "promise enough").
Therefore, let the result type of calling A with arguments (evaluating overloads, etc.) be RA.  If A is not callable with the arguments let RA be the narrowest return type possible for calling A.  Similarly define RB.  Then when A & B is called:

If neither A nor B are callable with arguments, the operation is an error (since neither side "promises enough").
If one or both of A and B is callable with the arguments, the return result is RA & RB.

Now, examine the various cases based on these rules:

When B <: A, case 2 is satisfied as long as B is callable with the arguments. The result type is RB as long as LSP is obeyed.
When neither is callable with the arguments, it's an error.
When both are callable with the arguments, it's the intersection.
When one is callable with the arguments:

class A:
    def f(self) -> RA:
        return 1

class B:
    def f(self, *, x: int = 1) -> RB:
        return 1
# (A & B).f() = RA & RB
# (A & B).f(x=1) = RA & RB

If both of A and B are callable with the arguments, the return result is RA & SB | RB & SA.

Can you explain this a bit? Why is it not RA & RB?

I don't think it's a good idea to get into the weeds about "signature compatibility". The rule for calling A & B should be based only on the validity and return values of A and B and no examination of parameter specifications.

This is part of the type. We have to have rules to resolve this. Ignoring the parameter specification does not work, examples of this above. We only know a minimum bound

When constructing the greatest lower bound signature of (A&B).foo, how exactly are @overload supposed to be taken into account? I have this bad anticipation that one will need a quadratic number of overloads. For example:
class X:
    @overload
    def foo(self, x: A) -> A: ...
    @overload
    def foo(self, x: B) -> B: ...
class Y:
    @overload
    def foo(self, x: C) -> C: ...
    @overload
    def foo(self, x: D) -> D: ...

then I think the greatest lower bound is something like
@overload
def foo(self, x: A) -> A & C & D: ...
@overload
def foo(self, x: B) -> B & C & D: ...
@overload
def foo(self, x: C) -> A & B & C: ...
@overload
def foo(self, x: D) -> A & B & D: ...

To keep this from growing exponentially, type-checkers likely won't get around aggressively simplifying. The second issue is that overloads are ordered.

@DiscordLiz In your example the issue of "compatible overlap" can actually be decoupled from the issue of working with paramspecs. We can restate the problem as follows:
class EmptyCall:
    def __call__(self): ...
class WaitCall(EmptyCall):
    def __call__(self, wait: bool = ...): ...
class A:
    foo: EmptyCall
    bar: WaitCall
class B:
    foo: WaitCall
    bar: EmptyCall

Here we used our domain knowledge of paramspecs to claim that all functions with a signature (wait: bool = ...) can be used where a () signature is expected, hence WaitCall is a subclass of EmptyCall. From this point on it doesn't really matter that these are about functions. The intersection A & B is indeed a type with {foo: WaitCall, bar: WaitCall}, meaning both of these are valid:
def foo(x: A & B):
  x.foo.__call__(wait=True)
    # we know x must be a B hence x.foo is known to be a WaitCall hence we can use its "method"
  x.bar.__call__(wait=True)
    # we know x must be an A hence x.bar is known to be a WaitCall hence we can use its "method"

@mniip If x is annotated as A&B then both x.foo and x.bar must be compatible with both WaitCall and EmptyCall.

@randolf-scholz if x.foo is compatible with WaitCall then it is also compatible with EmptyCall because of the subclass relationship.

@randolf-scholz your overloads there are incorrect, but it will still be complex and there is no way around this @erictraut brought concerns up with this in #5, and unfortunately we can now show this is necessary.
We can't fully express the true type here without a Not operator, but I'm still discussing with others on this, as Not presents a necessary behavior branch for Any

So, we need a Not as a prerequisite to this. Maybe we can leave this to the internals of typecheckers, but I think users should be able to express all types that the type system knows need exist.
The problem here comes from the following:
(A) -> A & (B) -> B
There is something which is consistent with both, that being
(T) -> T, for T: A XOR B
If A&B is passed to T, there is no return value consistent with the intersection of callables. While the function might be a passthrough of type and result in A&B, we can't know this to be the case.
This matters for typechecking usage in functions receiving an intersection.
XOR cannot be expressed without negation of some form, the easiest way would be Not
Current type theory would suggest that Not is only valid for Static Types.
I would thus recommend the following behavior:
If a gradual type with disjoint paramters is encountered in an intersection of callables, we have to widen it to the widest callable and lose type information. We could reject it, but this goes against gradual typing.
For all disjoint static type paramter lists, we can create what is a restricted overload set that explicitly disallows anything which would create a problem with resolving a return type as ambiguous use.
Not[Any] should be explicitly forbidden. Any internals which might create it should branch charitably to gradual typing before this point in time.

then I think the greatest lower bound is something like...

My point is that I think it should be possible to assess both the validity of the call and its return value (which is all you need) without synthesizing the entire interface.

My point is that I think it should be possible to assess both the validity of the call and its return value (which is all you need) without synthesizing the entire interface.

Unfortunately, this doesn't work. Rust's overlapping trait implementation resolution ran into a similar, yet different thing too, and they showed there were some cases where it was impossible to determine which implementation should be used correctly.
There are some parameters that we can't determine the correct return type for without more information than what the intersection provides, we can exclude those using the method @DiscordLiz explained above, either with bound type variables, or with overload sets.
Personally, I like the succinctness of the type variable method of consolidating them, but it may be less clear than the overload set for some people, and they are equivalent.

I think you can check this by assuming that the intersections A&B and C&D are empty

Where does this assumption come from? This is only possible to assume if there are class conflicts between intersection operands, and that these are concrete classes and not protocols.

I was typing something more than that, but instead, just a slight clarification to what @mikeshardmind just said above

There are some parameters that we can't determine the correct return type for without more information than what the intersection provides

it isn't just this.
(A&B) -> A is inconsistent with (A) -> A.
(A&B) ->B is inconsistent with (B) -> B.
(A&B) -> A&B can be consistent with each, but the intersection does not guarantee that it is from the minimum bound. An example where this would not be consistent:

class ParamA:
    ...

class ParamB:
    ...

class ParamAB(ParamA, ParamB):
    ...

class A:
    def foo(self, x: ParamA) -> ParamA:
        ...

class B:
    def foo(self, x: ParamB) -> ParamB:
        ...

class AB(A, B):
    @overload
    def foo(self, x: ParamA) -> ParamA:
        ...

    @overload
    def foo(self, x: ParamB) -> ParamB:
        ...

    def foo(self, x):
        if isinstance(x, ParamA):
            return ParamA()
        else:
            return ParamB()

def fn(x: A & B):
    x.foo(ParamAB())
    # if x is AB, this results in ParamA which is inconsistent with B.foo
    # even though AB.foo is consistent with A.foo and consistent with B.foo

Where does this assumption come from? This is only possible to assume if there are class conflicts between intersection operands, and that these are concrete classes and not protocols.

What I'm saying is that, suppose that to be the case, and then you can verify that the return type provided in the comment I quoted is too narrow.

Unfortunately, this doesn't work. Rust's overlapping trait implementation resolution ran into a similar, yet different thing too, and they showed there were some cases where it was impossible to determine which implementation should be used correctly.

I think it would be best to provide counterexamples.  I don't see why you need to synthesize the interface instead of just evaluating the validity and return type.  It's not as the user can display the interface.

@DiscordLiz @NeilGirdhar Yes that was wrong.
 So let's say we have this setup.
class X:
    @overload
    def foo(self, x: P) -> A: ...
    @overload
    def foo(self, x: Q) -> B: ...
class Y:
    @overload
    def foo(self, x: R) -> C: ...
    @overload
    def foo(self, x: S) -> D: ...

Then, by the lay of distributivity, we have
((P→A) | (Q→B)) & ((R→C) | (S→D))
= ((P→A)&(R→C)) | ((P→A)&(S→D)) | ((Q→B)&(R→C)) | ((Q→B)&(S→D))

which, if I followed the argument by @DiscordLiz, should be equivalent to something like
(P^R → A&C) | (P^S → A&D) | (Q^R → B&C) | (Q^S → B&D)

where ^ denotes XOR.
  Then one should have something like
PR = TypeVar("PR", P, R)
PS = TypeVar("PS", P, S)
QR = TypeVar("QR", Q, R)
QS = TypeVar("QS", Q, S)

@overload
def foo(self, x: PR) -> A&C: ...
@overload
def foo(self, x: PS) -> A&D: ...
@overload
def foo(self, x: QR) -> B&C: ...
@overload
def foo(self, x: QS) -> B&D: ...

With the issue that in the current form TypeVar cannot be used to specify XOR, as TypeVar("PR", P, R) only matches exactly P or exactly R, but not subtypes, and TypeVar("PR", bound=P|R) matches subtypes of the union, which is not the same as XOR. (see: https://github.com/python/typing/discussions/1260)

With the issue that in the current form TypeVar cannot be used to specify XOR, as TypeVar("PR", P, R) only matches exactly P or exactly R, but not subtypes, and TypeVar("PR", bound=P|R) matches subtypes of the union, which is not the same as XOR. (see: https://github.com/python/typing/discussions/1260)

Then this also needs resolving as a prerequisite of Intersections IMO. The below is necessary to express equivalence without exponential overload definitions.
T = TypeVar("T", (A | B) & ~(A & B))
Also, the not operator should be ~, we can argue if ^ should also exist as a shorthand for XOR, but we can show cases where ~T is directly useful, and I do not want to type object ^ T for this.

If both of A and B are callable with the arguments, the return result is RA & SB | RB & SA.

Can you explain this a bit? Why is it not RA & RB?

My thinking was that there could be a subclass of B that provides a broader parameter specification.  Consider:
class X: pass
class Y: pass
class Z(X,Y): pass
class A:
    def f(self, x: X) -> X:
        ...
class B:
    def f(self, x: Y) -> Y:
        ...
class Bprime(B):
    def f(self, x: Z) -> Z:
        ...

Now, what is (A&B).f(X)?  In this case, RB doesn't exist since B is not callable with X, but it's perfectly find to call it with Z, which is a subtype of X.

(The author of the intersection) can in fact view the interface with

That's fair, but the type checker can give a nebulous reply like [Callable intersection of X.foo and Y.foo], and leave it at that.   You don't need to generate the entire interface.

so the user (The author of the intersection) can in fact view the interface with

The type-checker could also just print the disjuntive normal form.

That's fair, but the type checker can give a nebulous reply like [Callable intersection of X.foo and Y.foo], and leave it at that. You don't need to generate the entire interface.

Please view my example above. We do in fact need the entire interface to check that use of the intersection is correct, and we do need to exclude some parameter types as causing inconsistent use with what is known.

@DiscordLiz In the example in https://github.com/CarliJoy/intersection_examples/issues/16#issuecomment-1660741476,
AB is not a proper subclass of A&B, this is just a reiteration of https://github.com/CarliJoy/intersection_examples/issues/16#issuecomment-1658766748.
The type checker can easily dismiss this as a subclass since e.g. the first overload does not return a subtype of ParamA & ParamB.

@DiscordLiz In the example in #16 (comment), AB is not a proper subclass of A&B, this is just a reiteration of #16 (comment). The type checker can easily dismiss this as a subclass since e.g. the first overload does not return a subtype of ParamA & ParamB.

This is incorrect. This is a valid subclass that passes type checking currently and which does not violate replacement under LSP or other models.

You don't need to "check that the intersection is correct". You might like to, but I don't think it's a requirement of implementing intersections. If someone types A&C, then it may be that nothing satisfies the interface. That's okay.

If we aren't checking that use of an intersection is correct, we may as well not have intersections. This is the entire point of static analysis.

@randolf-scholz My example differs from yours. yours, the parameter list is the same in each overload and is thus ambiguous.

@DiscordLiz Please carefully check the extended example in the issue.
  How is this not a blatent LSP violation?
from typing import overload
from collections.abc import Hashable, Sized

class A:
    def foo(self, x: Hashable) -> int:
        return hash(x)

class B:
    def foo(self, x: Sized) -> str:
        return f"x has {len(x)} elements"

class C(A, B):
    @overload
    def foo(self, x: Hashable) -> int: ...
    @overload
    def foo(self, x: Sized) -> str: ...

    def foo(self, x):  # type: ignore[no-untyped-def]
        if isinstance(x, Hashable):
            return A.foo(self, x)
        if isinstance(x, Sized):
            return B.foo(self, x)

# mypy does not complain about LSP
a_lsp: A = C()
b_lsp: B = C()
c: C = C()

# tuples are hashable and sized.
x: tuple[int, int, int] = (1,2,3)

# but the return types are incompatible!
reveal_type(a_lsp.foo(x))  # int
reveal_type(b_lsp.foo(x))  # str
reveal_type(c.foo(x))      # int, is not a subtype of str

If we aren't checking that use of an intersection is correct, we may as well not have intersections. This is the entire point of static analysis.

There are plenty of uses of intersections that can be checked.  Checking the "validity of an intersection" is one kind of thing that could be checked.  You might think that it's an important thing to check.
Nevertheless, I don't think that type specifications should not be making any claims about what should be checked.  They only need to make claims about how an interface is interpreted.  So this thread is in my opinion about how a method in two classes is resolved on the interface.  It's not about whether that intersection should raise an error.  That seems to be a different question that should probably discussed separately.

Nevertheless, I don't think that type specifications should not be making any claims about what should be checked. They only need to make claims about how an interface is interpreted. So this thread is in my opinion about how a method in two classes is resolved on the interface. It's not about whether that intersection should raise an error. That seems to be a different question that should probably discussed separately.

The who are inseparable. I cannot check if (A&B).foo(1) is valid without us first having a specification for what (A&B).foo is

How is this not a blatent LSP violation?

On a closer look, this raises the same issue intersections do and shows the need for Not

@DiscordLiz

The two are inseparable. I cannot check if (A&B).foo(1) is valid without us first having a specification for what (A&B).foo is

It is A.foo & B.foo. The type checker can test if x≤A&B by testing x.foo≤A.foo and x.foo&B.foo, without ever constructing this shared interface. Likewise, when you assume x is A&B and call x.foo(<inputs>), the typechecker can simply separately go through the overloads of A and figure out the induced return type of RA of A.foo for <inputs> and likewise RB of B.foo for  <inputs>. Then the actual return type of x.foo(<inputs>) is RA & RB.

@randolf-scholz A counter example that shows this is still possible. Keep in mind that raising an error is not incompatible with a type
class ParamA:
    ...

class ParamB:
    ...

class ParamAB(ParamA, ParamB):
    ...

class A:
    def foo(self, x: ParamA) -> ParamA:
        ...

class B:
    def foo(self, x: ParamB) -> ParamB:
        ...

class AB(A, B):
    @overload
    def foo(self, x: ParamA) -> ParamA:
        ...

    @overload
    def foo(self, x: ParamB) -> ParamB:
        ...

    def foo(self, x):
        if isinstance(x, ParamA):
            if isinstance(x, ParamB):
                raise RuntimeError("Ambiguous use")
            return ParamA()
        else:
            return ParamB()

The two are inseparable. I cannot check if (A&B).foo(1) is valid without us first having a specification for what (A&B).foo is

It's up to the type checker to decide how it does its job.  It can leave the object in an abstract (non-concrete) state and then resolve the type in a lazy way if it wants.  The specification should not be telling type checkers how to do their job, in my opinion.
All you need to specify is what the return type of (A&B).foo(P) is for some given P where P is a possibly-empty parameter specification.
Nowhere does resolving this require synthesizing an interface.

It's up to the type checker to decide how it does its job.

That's true. We still need a specification for what A&B means. Without this, what's the point? constructing it lazily is possible, but it still needs to be constructed when I reach use of it. We need a definition for what this is stil

Nowhere does resolving this require synthesizing an interface.
https://github.com/CarliJoy/intersection_examples/issues/16#issuecomment-1660807281

We still need a specification for what A&B means.

In this thread, we're asking what to do when a method is called on A&B given parameters P.  We don't need a specification for A&B to figure that out.  We just need to know whether the call is an error, and what the return type is.

Okayyyy. I'm gonna recommend everyone take a step back again, this seems to be getting heated.
@randolf-scholz @NeilGirdhar: @DiscordLiz has actually shown an example here that isn't an LSP violation and requires synthesizing an interface. It did take a bit of back and forth to reach, but we were working on the abstract form that showed this had to be the case prior to coming up with a correct python code example in other conversation. If you'd like to join this conversation, we should probably have more people participating in the discord conversations that have predated github posts.
CC: @erictraut you may want to see this example as well.

In this thread, we're asking what to do when a method is called on A&B given parameters P. We don't need a specification for A&B to figure that out. We just need to know whether the call is an error, and what the return type is.

This requires knowing what the return type for P is which requires the minimum bound. Please do not disregard what other people have said without at least showing an example that proves otherwise first. We're all trying to make intersections be good, and disregarding what people have said is not constructive to that goal.

@DiscordLiz You've essentially added an overload of the form foo(self, x: ParamA & ParamB) -> Never:.
This is allowed, the same is possible without any intersection whatsoever:
from typing import Never

class A:
    def foo(self, x: int) -> int: return 0
class B(A):
    def foo(self, x: int) -> Never: raise RuntimeError

x: A = B()

passes type-checking, even with --strict.

@DiscordLiz You've essentially added an overload of the form foo(self, x: ParamA & ParamB) -> Never:.
This is allowed, the same is possible without any intersection whatsoever:
from typing import Never

class A:
    def foo(self, x: int) -> int: return 0
class B(A):
    def foo(self, x: int) -> Never: raise RuntimeError

x: A = B()

passes type-checking, even with --strict.

This is not the effect of this, but it does show that there can still be a subclass with fits replacement for both in python. Some of the types are poorly expressible without Not.

@DiscordLiz upon second thought, I think the issue with your example is that it is poorly typed. For a static type checker
class AB(A, B):
    @overload
    def foo(self, x: ParamA & ParamB) -> Never: ...
    @overload
    def foo(self, x: ParamA) -> ParamA: ...
    @overload
    def foo(self, x: ParamB) -> ParamB: ...

and
class AB(A, B):
    @overload
    def foo(self, x: ParamA) -> ParamA: ...
    @overload
    def foo(self, x: ParamB) -> ParamB: ...

Are not the same thing. The former would be a valid subclass of A&B, the latter isn't. We can verify this by using the same steps a typechecker would do.
And a type checker should complain that
@overload
def foo(self, x: ParamA) -> ParamA: ...
@overload
def foo(self, x: ParamB) -> ParamB: ...

is not a subtype of def foo(self, x: ParamB) -> ParamB: ..., but the triple overload
@overload
def foo(self, x: ParamA & ParamB) -> Never: ...
@overload
def foo(self, x: ParamA) -> ParamA: ...
@overload
def foo(self, x: ParamB) -> ParamB: ...

is.

And the answer to that question is ParamA & ParamB. That the code refuses to return that is not relevant. The type checker could optionally detect that the overload resolution is ambiguous, but it doesn't have to do that either.

It is ambiguously ParamA & ParamB or Never. Both are consistent options, and it is impossible to know which you have from just A&B

@DiscordLiz upon second thought, I think the issue with your example is that it is poorly typed. For a static type checker
class AB(A, B):
    @overload
    def foo(self, x: ParamA & ParamB) -> Never: ...
    @overload
    def foo(self, x: ParamA) -> ParamA: ...
    @overload
    def foo(self, x: ParamB) -> ParamB: ...

and
class AB(A, B):
    @overload
    def foo(self, x: ParamA) -> ParamA: ...
    @overload
    def foo(self, x: ParamB) -> ParamB: ...

Are not the same thing. The former would be a valid subclass of A&B, the latter isn't.
We can verify this by using the same steps a typechecker would do. The type checker should complain that

I agree that it is ambiguously (poorly) typed, but it is the best that is possible currently. Treating this as an error prior to providing a way to give the type checker enough information seems incorrect to me.

It's not surprising that multiple inheritance cannot be properly typed prior to intersection types. However, not treating it as an error, which it is, can lead to false-negative LSP violations, so pick your poison.

False Negatives are probably less disruptive that False positives here. I assume that anyone with a complex multiple inheritance case that is using static analysis also has tests for complex sections that would catch an issue with this.

False Negatives are probably less disruptive that False positives here. I assume that anyone with a complex multiple inheritance case that is using static analysis also has tests for complex sections that would catch an issue with this.

But how would you even encounter errors against A&B since A&B currently does not explicitly exist in the typing system? Likely, your code is annotated with AB and not A&B in this case, so no issues there. If this is fixed, at the worst you'd have to add 1 type: ignore to the class definition.

Assuming the user to be incorrect when there is a way for them to be correct seems extremely hostile for a CI action to me.

It happens all the time that when you update the type-checker to a new version that it can find a few more things, or previous type: ignore become superfluous...

But this isn't finding a new thing, it's a bad heuristic that assumes the user must have made a mistake in the implementation when the user has provided information saying they handle both cases.

Not really a productive tangent. I see you've submitted your case to not do this to both type checkers, lets try and get this pep forward so that it can be expressed clearly

I'm going to summarize where I think we are on this, if everyone could hold off on further comments for a moment, I'd like to get a summary together. Then we can check if anyone disagrees with the summary before continuing and try to continue this from a place focused on answering the question this issue poses.

We need a specification for resolving attributes of (A&B) to prevent diverging behavior in type checkers. This can result in a library interface by @erictraut 's definitions in another comment that I can find later, so it does need to be consistent between type checkers, and therefore does need specifying.

There appears to be a case for Needing Not (~), and maybe for convenience XOR (^) These need to be incompatible with gradual types under our current knowledge, but we can do this in a way that protects the use case for gradual typing still. This case appears to be necessary with or without intersections due to correctly expressing overloads involving disjoint parameter specifications in overlapping method definitions with multiple inheritances.

For an intersection of callables with statically typed parameter specifications, the resulting intersection needs to be careful to avoid a case that also arises with overloads currently, and is a motivating factor for Not (~) and XOR (^). If these are directly accepted as well, it becomes easier for users to properly mark compatibility by additionally intersecting a protocol indicating the behavior they expect in the divergent case.

While it would be nice to avoid losing information, the gradual typing should not have a special exception as this is already highly complex to resolve in the case of callables. We yield to the gradual typing in this case, even if it might (through highly complex options) be possible to determine slightly more. As a result, Any & Callable (and similar gradual typing formations involving Callables) -> Any

Use of an intersection's attributes is behavior that needs to be checked.

def f(x: A & B):
    x.foo(1)  # this needs to be checked

Assignment to an intersection needs to be checked:

def f(x: A & B):
    ...

f(foo())  # this needs to be checked

x: A & B = Foo()  # this needs to be checked

5 & 6 do not need to happen at the same time

5 requires synthesizing the interface foo, and also will require NOT. The interface is only required to be synthesized at use to check the use and does not require synthesizing all interfaces to try and determine that the intersection itself may be problematic. (though we leave this option to type checkers should they want this)

If anyone disagrees with this summary of the situation, please specify which you disagree with and provide a direct example that takes into account prior discussion in this thread.

While XOR was considered by multiple people who thought about it as mostly a convenience (myself included at first), I think it also helps with concise and expressive relationships. In support of XOR as not superfluous with NOT, copying the following from Discord:
It also provides a really good answer for how to express the actual type of
(A) -> A & (B) -> B & (C) -> C

that being:
(T) -> T, T: A ^ B ^ C

In response to "whats wrong with just leaving it as the first apart from maybe being a bit verbose?"
The latter helps show to users the ambiguous case we need to disallow without an additional intersecting protocol clarifing ambiguity
For instance:
(A & ~B) -> A & (B & ~A) -> B & (A & B) -> A & B

Becomes:
(T) -> T, T: A | B

but
(A & ~B) -> A & (B & ~A) -> B & (A & B) -> Never

remains:
(T) -> T, T: A ^ B

(I don't want to expand this to A&B&C by hand) [meaning the callables]

A couple of points:

I am not convinced NOT / XOR is needed to move forward with intersections. The example brought forward so far can be resolved without the need for NOT. (though I would like having NOT, I think this would hinder this PEP quite a lot).
 Details....
The behavior of the intersection of two callables (X ↦ A) & (Y↦B) can be summarized by the following table:

Input
Output

X&Y
A&B

X
A

Y
B

This can be realized via triple overload without the need for NOT:
@overload  X&Y ↦ A&B
@overload  X ↦ A
@overload  Y ↦ B

Note that even with XOR, typing the intersection case X&Y remains necessary.

I am not convinced that synthesizing interfaces is required, as per https://github.com/CarliJoy/intersection_examples/issues/16#issuecomment-1660806394.
 Details....
The example brought forth in https://github.com/CarliJoy/intersection_examples/issues/16#issuecomment-1660807281, the class AB violates LSP for A&B since the crucial intersection overload ParamA & ParamB ↦ Never is missing.
For any reasonable definition of intersection, we must have
def fn(x: A & B):
revel_type( x.foo(ParamAB()) )   # must reveal ParamA & ParamB

Note that this is compatible with adding the missing overload, as Never ≤ ParamA & ParamB.

There seems to be some confusion about overloads being equivalent to intersection. But overloads are ordered while intersections aren't, hence they are not equivalent.
 Details....
@overload
def foo(x: A) -> A: ...
@overload
def foo(x: B) -> B: ...

is not the same as foo: Callable[[A], A] & Callable[[B], B], since the former returns A for input A&B, but the latter returns A&B.

If anyone disagrees with this summary of the situation, please specify which you disagree with and provide a direct example that takes into account prior discussion in this thread.

Thanks for your summary. Here's my take:

We need a specification for resolving attributes of (A&B) to prevent diverging behavior in type checkers.

Yes. I think most people agree that the attributes are the common ones, with intersection to deal with conflicts?

There appears to be a case for Needing Not (~), and maybe for convenience XOR (^)

I don't know, but that should be discussed in a separate issue.

For an intersection of callables with statically typed parameter specifications, the resulting intersection needs to be careful to avoid a case that also arises with overloads currently, and is a motivating factor

This seems to be about bad intersection detection.  If you want to discuss this, it should be discussed in a different thread in my opinion.  I don't see how this relates to the issue text.  And my opinion is that this is a type feature request.  It's not required for consistent interpretation of intersections.

...Any & Callable (and similar gradual typing formations involving Callables) -> Any

I don't see how this follows from the type rules.  After all, many people argued that Any & T is irreducible.

Use of an intersection's attributes is behavior that needs to be checked.

Yes.  The calling example you gave is exactly what I think this thread is about.

Assignment to an intersection needs to be checked:

Yes.  I think it should be a different thread.

5 requires synthesizing the interface foo,

I don't agree.

I directly requested that people keep in mind the prior conversation. Both of you @NeilGirdhar @randolf-scholz have referenced a comment that was followed with a case that showed this needs synthesizing, provided by @DiscordLiz .
If you disagree with this comment, please show how the example she provided here and her explanation of the divergent behavior here does not require finding the minimum bound to ensure safe use.
Talking over other people's ideas without addressing the cases they present is not helpful.

Not is difficult because it requires additional proofs to be part around.
Consider:
Class AB(A, B):...

a1 : A = AB()
a2 : A = A()

def f(a : A & ~B):...

# n-levels deep:
f(a1) # this must be an error.
f(a2) # therefore so must this.

It seems like the overhead in passing around a negative type consistently will leave it as a rather niche concern, especially as it typically needs an intersection to be meaningfully useful.
Xor may be easier because it has a more direct usage in disjoint unions (and sound behaviour with current unions can be annoying when you know things are disjoint).  Though it still suffers from being hard to construct.
But this seems a bit beside the point since neither are necessary for intersections to be introduced (they have more value for overloads than for intersections).  So any discussion of them could/should be done separately.

@NeilGirdhar

For an intersection of callables with statically typed parameter specifications, the resulting intersection needs to be careful to avoid a case that also arises with overloads currently, and is a motivating factor
This seems to be about bad intersection detection. If you want to discuss this, it should be discussed in a different thread in my opinion. I don't see how this relates to the issue text. And my opinion is that this is a type feature request. It's not required for consistent interpretation of intersections.

It is not. This issue is about how to resolve intersections with callable. This is a necessary concern of that.

@mikeshardmind I already responded to this in https://github.com/CarliJoy/intersection_examples/issues/16#issuecomment-1660840763, the problem is that the class AB should be dismissed as a subclass of A&B due to the missing overload. The given list of overloads for AB.foo is incompatible with (A&B).foo. The given runtime implementation is irrelevant as we are talking about static type checking. If the missing overload def foo(self, x: ParamA & ParamB) -> Never: ... was added, then everything would be fine. A type-checker would hint x.foo(ParamAB()) as ParamA & ParamB, because (ParamA ↦ ParamA) & (ParamB ↦ ParamB) evaluated for input ParamAB (≤ ParamA & ParamB) returns ParamA & ParamB.

If you disagree with this comment, please show how the example she provided here and her explanation of the divergent behavior here does not require finding the minimum bound to ensure safe use.

I answered her case here.

This issue is about how to resolve intersections with callable. This is a necessary concern of that.

I added a paragraph to clarify the "divergent behavior"

@diabolo-dan
It was shown via formal logic in discord on abstract types that negation is neccessary, and has not been fully typed up yet. (I have opened a separate issue for this #17 when those are ready) however this issue has a few examples of where it is needed for correct expression. I believe that a couple of people are working on typing up the exact abstract formalisms to include as well, but we have examples in the thread based on python code.
Keep in mind that either XOR or NOT being implemented would be equivalent to both in the presence of both Unions and Intersections.

I directly requested that people keep in mind the prior conversation. Both of you @NeilGirdhar @randolf-scholz have referenced a comment that was followed with a case that showed this needs synthesizing, provided by @DiscordLiz .
If you disagree with this comment, please show how the example she provided here and her explanation of the divergent behavior here does not require finding the minimum bound to ensure safe use.
Talking over other people's ideas without addressing the cases they present is not helpful.

(not aimed at me, but...)
Wrt: https://github.com/CarliJoy/intersection_examples/issues/16#issuecomment-1660807281
It was already responded to in the thread that the result should be ParamA & ParamB.  The response in the second link is that it could be Never.   Never is compatible with ParamA & ParamB so that doesn't appear to be problematic.
This can cause a runtime exception, but the type system in general can't prevent runtime exceptions.
Consider:
class A:
    def foo(self, x:int | float) - > int | float:
        return x

class B(A):
    def foo(self, x:int | float) - > int:
        assert isinstance(x, int)
        return x

From a type perspective, the latter is substitutable.  I can't see a solution to that without adding exception information to the type system.

@NeilGirdhar

If you disagree with this comment, please show how the example she provided here and her explanation of the divergent behavior here does not require finding the minimum bound to ensure safe use.

I answered her case here.

Prior to this addition, you did not actually address what she presented.

This issue is about how to resolve intersections with callable. This is a necessary concern of that.

I added a paragraph to clarify the "divergent behavior"

I believe the synthesis to still be necessary currently. I think there is an outcome from the handling of NOT and/or XOR which would make your conclusion correct, and an outcome from it which would make it incorrect. Can we mark this as something to revisit after the dependency we seem to have on negation in some form?
@randolf-scholz I think the above applies to your response as well with

@mikeshardmind I already responded to this in https://github.com/CarliJoy/intersection_examples/issues/16#issuecomment-1660840763, the problem is that the class AB should be dismissed as a subclass of A&B due to the missing overload. The given list of overloads for AB.foo is incompatible with (A&B).foo. The given runtime implementation is irrelevant as we are talking about static type checking. If the missing overload def foo(self, x: ParamA & ParamB) -> Never: ... was added, then everything would be fine. A type-checker would reveal x.foo(ParamAB()) as ParamA & ParamB, because (ParamA ↦ ParamA) & (ParamB ↦ ParamB) evaluated for input ParamAB (≤ ParamA & ParamB) returns ParamA & ParamB.

I think resolving this is predicated on if NOT is typechecker internal or part a dependent part of this PEP. If it is internal, there would be no way to express this, so I think we need to revisit this after #17

Can we mark this as something to revisit after the dependency we seem to have on negation in some form? (#17)

Yes, you should maybe create a whole issue for it!

@NeilGirdhar Again, I don't think NOT is necessary, see the "Details" section in https://github.com/CarliJoy/intersection_examples/issues/16#issuecomment-1660969453. But maybe an example can be provided that shows otherwise.
In some sense, the necessary exclusion behavior of NOT is emulated by the order-dependence of @overload. By having an overload of the form X&Y ↦ ... first, it follows that in any subsequent overload such as X ↦ ... is equivalent to X & ~Y ↦....
I would agree that having NOT/XOR explicitly available would make it easier to write correct overloads, since one could then explicitly split the input into pairwise disjoint sets.

@diabolo-dan

It was already responded to in the thread that the result should be ParamA & ParamB. The response in the second link is that it could be Never. Never is compatible with ParamA & ParamB so that doesn't appear to be problematic.

I find this unsatisfactory for the same reasons that NoReturn exists. Things that should always error are different from things that have the potential to error, but I think that if we add Not as user expressible, that I'm fine with it. I know that isn't quite consistent with the current type system, but we are also having a lot of discussions about changing some of the current definitions to be more expressive and formalized, and I think there is value in the difference between always an error and may be an error.

@diabolo-dan
It was shown via formal logic in discord on abstract types that negation is neccessary, and has not been fully typed up yet. (I have opened a separate issue for this #17 when those are ready) however this issue has a few examples of where it is needed for correct expression. I believe that a couple of people are working on typing up the exact abstract formalisms to include as well, but we have examples in the thread based on python code.

Unfortunately I'm not privy to the discord discussions.  I have not seen any examples in this thread that indicate it to be necessary for intersections. (yes I have read the whole thread).
What in particular is Negation shown to be necessary for?

@randolf-scholz

@mikeshardmind Again, I don't think NOT is necessary, see the "Details" section in https://github.com/CarliJoy/intersection_examples/issues/16#issuecomment-1660969453. But maybe an example can be provided that shows otherwise.
In some sense, the necessary exclusion behavior of NOT is emulated by the order-dependence of @overload. By having an overload of the form X&Y ↦ ... first, it follows that in any subsequent overload such as X ↦ ... is equivalent to X & ~Y ↦....

and @diabolo-dan

What in particular is Negation shown to be necessary for?

We still need a way to express the difference between
(A & ~B) -> A & (B & ~A) -> B & (A & B) -> A & B

and
(A & ~B) -> A & (B & ~A) -> B & (A & B) -> Never

And we can't preserve the order of overloads in an intersection without this losing symmetry with Unions.
X | Y = Y | X, X & Y, but the order of overloads matters? This also may have consequences on distributivity.

@mikeshardmind first, I just want to note that I find this notation a bit hard to read. I would prefer it it was possible to bracket around the whole function. I will assume you mean
①: ((A&~B) ↦ A) & ((B&~A) ↦ B) & ((A&B) ↦ (A&B))
②: ((A&~B) ↦ A) & ((B&~A) ↦ B) & ((A&B) ↦ Never)
③: ((A&~B) ↦ A) & ((B&~A) ↦ B) & (A & B) -> C

in any case, since the input domains are pairwise-disjoint these correspond in tabular form to

Input
Output ①
Output ②
Output ③

A&B
A&B
Never
C

A\B
A
A
A

B\A
B
B
B

Which can be implemented via overloads as follows:
@overload: A&B →A&B
@overload: A →A
@overload: B →B

and
@overload: A&B →Never
@overload: A →A
@overload: B →B

and
@overload: A&B →C
@overload: A →A
@overload: B →B

So I really don't get what the hangup is, these seem really straightforward.

@mikeshardmind
I don't believe the order of overloads does or should matter.  An object can be validly assigned to a type A & B iff it can be proved that it can be assigned to a type A and it can be proved that it can assigned to a type B.
If A and B have overrides as part of their definition, then the object must be compatible with both sets of overrides.

@diabolo-dan

I don't believe the order of overloads does or should matter.

@NeilGirdhar

Which can be implemented via overloads as follows:

@overload: A&B ↦ A&B
@overload: A ↦ A
@overload: B ↦ B

The ordering of overloads actually matters or multiple overloads apply to the same case We can remove the ordering concern by changing this to
(overload) (A & B) -> A & B
(overload) (A & ~ B) -> A
(overload) (B & ~A) -> B
Absent this, then we actually probably need the synthesis for ordering not to matter as the type checker needs to confirm that the overloads below are all consistent for the case of A & B, and determine which is the most specific of them.
(overload) (A & B) -> A & B
(overload) (A) -> A
(overload) (B) -> B
as all three would apply.

Beyond this, I believe there are other reasons it is needed that I would defer to those who came up with them to add in #17

And we can't preserve the order of overloads in an intersection without this losing symmetry with Unions. X | Y = Y | X, X & Y, but the order of overloads matters? This also may have consequences on distributivity.

Yes, the order of overloads matter, it always has, and it's not something that is going to disappear...

@DiscordLiz

I find this unsatisfactory for the same reasons that NoReturn exists. Things that should always error are different from things that have the potential to error, but I think that if we add Not as user expressible, that I'm fine with it. I know that isn't quite consistent with the current type system, but we are also having a lot of discussions about changing some of the current definitions to be more expressive and formalized, and I think there is value in the difference between always an error and may be an error.

I'm not convinced it's worth giving up type consistency to pick up always exceptions, or that it's plausible in many cases.
That said, in this case, it could relatively early be picked up when you implement a subclass of A and B.  If the type checker is working correctly, it'll require you to have your information that includes consistency with A&B -> A&B. You can type that as A&B - > Never and all you'd need is a type check or other linter can be set up to pick up -> Never.

The ordering of overloads actually matters

Sorry, I meant to say the order of overloaded intersections.  Within each part of the intersection which has overloads, the order is of course important.
I thought the order of the intersection was what you were taking about from the context, but correct me if I may have misunderstood.

@diabolo-dan

I'm not convinced it's worth giving up type consistency to pick up always exceptions, or that it's plausible in many cases.

Normally I would agree, but I think this is a case where the two possible handlings are mutually exclusive, so we should have a way to express which one we intend.
@mikeshardmind

Beyond this, I believe there are other reasons it is needed that I would defer to those who came up with them to add in https://github.com/CarliJoy/intersection_examples/issues/17

There are, but I'm getting sick of this discussion if you attempt to summarize it, I'll revisit for any needed corrections later.

@diabolo-dan

The ordering of overloads actually matters

Sorry, I meant to say the order of overloaded intersections. Within each part of the intersection which has overloads, the order is of course important.
I thought the order of the intersection was what you were taking about from the context, but correct me if I may have misunderstood.

If we can't remove the order dependency, then we can't have consistent handling for an intersection between two things that both have overloads. How does the intersection determine which operand's overloads are "first"? Do we require intersections to be ordered?
Restricting what is "knowable", and implicitly handling the disjointedness removes the ordering concern and allows normal algebraic operations to function, something I would consider a non-negotiable feature. This doesn't need to be exposed to users as a type they can use, but I think I agree with @DiscordLiz that users should be able to express with Not to help with other ambiguous cases.
This lack of well-ordering and resolving what is safe with multiple definitions has prior art and requires that we can show that interfaces are either disjoint or specializations of each other.

@mikeshardmind

If we can't remove the order dependency, then we can't have consistent handling for an intersection between two things that both have overloads. How does the intersection determine which operand's overloads are "first"? Do we require intersections to be ordered?

There are precisely 2 questions to be answered:

When is C.foo a subtype of (A&B).foo?
Answer: if and only if C.foo is a subtype of both A.foo and B.foo
Type-checkers should already know how to do this, even when A.foo, B.foo and C.foo are overloaded.

If (A & B).foo is called with arguments args, what is the return type?
Answer: it's the type of A.foo(args) & B.foo(args). All the type checker needs to do is, in order, go through the list of overloads of A.foo until it finds a match for args and note the return type as RA, then do the same for B.foo to get RB. The end result is RA & RB

What is not covered is how you would synthesize a compatible subclass of A&B automatically, but that should be outside the scope of the PEP, imo. The only reasonable exception I would consider is providing an auto-merge function for disjoint Protocol-classes. Later PEPs can extend this functionality if needed.

If (A & B).foo is called with arguments args, what is the return type?
Answer: it's the type of A.foo(args) & B.foo(args). All the type checker needs to do is, in order, go through the list of overloads of A.foo until it finds a match for args and note the return type as RA, then do the same for B.foo to get RB. The end result is RA & RB

This does not work, If you still do not understand why from my earlier comment about "well ordered overloads" necessitating an ordering on intersections, then please wait until more details explaining this are added to #17. A lot of work went into proving the necessity of this, and there is prior art also showing the necessity of this in a static typed environment. This is going to take some time to explain in proper detail with formal proof.

This lack of well-ordering and resolving what is safe with multiple definitions has prior art and requires that we can show that interfaces are either disjoint or specializations of each other.

Note that the linked rfc is about dispatch for multiple implementations, not about type compatibility.  Dispatch needs to be unambiguously ordered because only a single implementation should get called.  That doesn't apply for Type compatibility.
Wrt to your other points I'll wait for #17 to be fleshed out as requested.

@diabolo-dan

Note that the linked rfc is about dispatch for multiple implementations, not about type compatibility. Dispatch needs to be unambiguously ordered because only a single implementation should get called. That doesn't apply for Type compatibility.

Rust's method of dispatch on implementation is about unambiguously selecting an implementation based on type compatibility and specificity of the implementation. There's a subtle relation, and I acknowledge it is not 1:1, but it is a useful framework for correctly determining the most accurate/specific overload or intersected implementation for a given parameter list* We then have the return type (This is "to what end") of that overload or intersected implementation as our most specific known knowledge. (rather than dispatching to that, it still only goes to whatever is on the mro of the type)

Something I found when implementing my simulator - I've been thinking that Callable & Callable would become an overload of __call__, similarly to:
class A:
    def __call__(self, x: int) -> int:
        ...

class B:
    def __call__(self, x: str) -> str:
        ...

(A & B).__call__

But I've now realised the call method isn't quite analogous to:
Callable[[int], int] & Callable[[str], str]

Because in the above the argument isn't named. It's kind of like call but with wildcards for arg names.
My conclusion from this is maybe it's a different beast, that behaves similarly - so it still clashes like overloads would etc, but it's not in itself an overload.

@mark-todd You probably use Callback-Protocols in your simulator, instead of Callable[[int], int] & Callable[[str], str]
class Call_A(Protocol):
    def __call__(self, x: int) -> int:
        ...

class Call_B(Protocol):
    def __call__(self, x: str) -> str:
        ...

(A & B).__call__  # Call_A & Call_B

Simplifying Call_A & Call_B to the single callback protocol representing the __call__ of A&B will in general be very challenging, since compatible signatures need to be determined and overloads need to be taken into account.
  Example
@overload
def f(arg: A) -> U: ...
@overload
def f(arg: B) -> V: ...

@overload
def g(arg: S) -> X: ...
@overload
def g(arg: T) -> Y: ...

Then f&g maps types according to the table

arg
return

A & S
U & X

A & (T\S)
U & Y

(B\A) & S
V & X

A \ (S | T)
U

S \ (A | B)
X

B \ (A | S | T)
V

T \ (S | A | B)
Y

(B\A) & (T\S)
V & Y

Which can be translated into the following equivalent set of 8 overloads (without using NOT / set differences)
(A & S) -> (U & X)
(A & T) -> (U & Y)
(B & S) -> (V & Y)
         A -> U
         S -> X
(B & T) -> (V & Y)
         B -> V
         T -> Y

You can verify this by drawing a 4-component Venn diagram:

It should be possible to give a tight upper bound for the number of needed overloads, I'll think about this over the holidays.

Because in the above the argument isn't named. It's kind of like __call__ but with wildcards for arg names.

Since Callable can only express positional argument types (except for Callable[..., X], which is special), the equivalent __call__ method would be something like this:
    def __call__(self, x: int, /)...

For now I've implemented something separate in the simulator for the Callable type, but it follows the same logic as __call__ would. The only part I have figured out yet is how to resolve something like the below:
class A:
    def __call__(self, x: int, /) -> int:
        pass

a = Intersection[A, Callable[[str], str]]

I suppose if the forward slash makes it positional only that's comparable, but if I'm to make one overloaded method, what's the arg name? I suppose it could take the name from the side that does have a name to produce:
Overload[(self, x: int, /) -> int, (self, x: str, /) -> str]

Overload[(self, x: int, /) -> int, (self, x: str, /) -> str]

Where does that notation come from? Are you just making something up? I don't think it was in the callable signature PEP 677, though during its development we considered something like this.
I'm also confused by this, since in the example neither side has a name:

if I'm to make one overloaded method, what's the arg name? I suppose it could take the name from the side that does have a name to produce

Where does that notation come from? Are you just making something up? I don't think it was in the callable signature PEP 677, though during its development we considered something like this.

I could be wrong, but I believe this is how pyright shows method types when you inspect them - if the method is overloaded it uses the notation I used here.

I'm also confused by this, since in the example neither side has a name:

When I say name I mean x in the __call__ method - I only noticed the difference when I came to construct a function signature from a callable, and discovered I had to generate a new name. Callable defines the type of the argument but not the name.

I could be wrong, but I believe this is how pyright shows method types when you inspect them - if the method is overloaded it uses the notation I used here.

Okay, that's hardly canonical. :-)

When I say name I mean x in the __call__ method - I only noticed the difference when I came to construct a function signature from a callable, and discovered I had to generate a new name. Callable defines the type of the argument but not the name.

But the name x in (self, x: str, /) -> str is not part of the type, it's just there for documentation -- you might as well use _. Or switch to the PEP 677 notation, which I suppose would be (Self, str) -> str (not sure what to do for self, actually -- why is it in the signature?)

But the name x in (self, x: str, /) -> str is not part of the type, it's just there for documentation -- you might as well use _. Or switch to the PEP 677 notation, which I suppose would be (Self, str) -> str (not sure what to do for self, actually -- why is it in the signature?)

Yeah that makes sense - I'm not sure if Self should be there really, as like you say in method form it's not really an input arg. So let's say we switch to: (str) -> str
Is there a canonical type for overloads? I've seen pyright use Overload but as you say that may not be following the PEPs. With the introduction of this function: https://docs.python.org/3/library/typing.html#typing.get_overloads
They even exist at runtime (I suppose the return type of this is list[Callable])
Radical thought perhaps but if there isn't such a notation do we need to introduce one? It could be:
Overload[(str) -> str, (int) -> int]. I would suggest we use intersection here but overloads have a defined order.

On the below I get a type error:
from typing import Callable, overload
class A:
    @overload
    def foo(self, x: int) -> int:
        ...

    @overload
    def foo(self, x: str) -> str:
        ...

    def foo(self, x: int | str) -> int | str:
        return 1

x: Callable[[int | str], int | str] = A.foo

Expression of type "Overload[(self: A, x: int) -> int, (self: A, x: str) -> str]" cannot be assigned to declared type "(int | str) -> (int | str)"
  No overloaded function matches type "(int | str) -> (int | str)"Pyright[reportGeneralTypeIssues]

On closer inspection, I think I've worked out what's going on here (at least in pyright). Interestingly enough, I have a feeling that the type of foo is already basically an intersection in it's current implementation:
from typing import Callable, overload
@overload
def foo(x: int) -> int:
    ...

@overload
def foo(x: str) -> str:
    ...

@overload
def foo(x: int | str) -> int | str:
    return 1

def foo(x: int | str) -> int | str:
    return 1

x: Callable[[str], str] = foo

The above returns no errors - and interestingly, if I make the final line:
x: Callable[[int], int] = foo

or
x: Callable[[int | str], int| str] = foo

These also return no errors. Since all of these are acceptable, I propose that the type of foo is effectively already: Callable[[str], str] & Callable[[int], int] & Callable[[int | str], int| str], and that's why assigning the type to part of the intersection is valid.

@mark-todd overloads are order-dependent and thus generally not equivalent to intersections. In your example, this is somewhat masked by the fact that one cannot define a nominal subtype of the built-ins int and str in python.
@overload
def foo1(x: X) -> X: ...
@overload
def foo1(x: Y) -> Y: ...

@overload
def foo2(x: Y) -> Y: ...
@overload
def foo2(x: X) -> X: ...

foo3: Callable[[X],X] & Callable[[Y], Y]

All describe different behaviors. foo1(X&Y) gives X, foo2(X&Y) gives Y and foo3(X&Y) gives X&Y.

@mark-todd overloads are order-dependent and thus generally not equivalent to intersections. In your example, this is somewhat masked by the fact that one cannot define a nominal subtype of the built-ins int and str in python.

Ah yep apologies I remember previous discussions about ordering in Overloads now - in that case I think we're back to the Overload type so:
Overload[Callable[[int], int], Callable[[str], str], Callable[[int | str], int| str]]]

I have to admit I don't know what you're trying to accomplish here (didn't look at the simulator PR) and I don't know your constraints, but that's not an accurate representation of that intersection type, right? In Callable[[int | str], int| str]] we might get str back for an int, which neither the overload nor the intersection does.

I mentioned this in discord earlier, but when trying to represent an intersection of callables with the simplest equivalent overloads, the ordered list for the simplest case
(T) -> T & (U) -> U

is
overload (T & U) -> T & U
overload (T) -> T
overload (U) -> U
implementation (T | U) -> T | U

The intersection form should be prefered rather than expanding it to this.

I'm tentatively closing this one. I believe the consistency based rule we are aiming for ends up solving this sufficiently, can be reopened if there are new relevant issues to this topic that would benefit from being continued here.


Error: HikConnect needs account and password specified in config

how to configure username and password
there is no spot for username and password, should i add it manually ?
Thank you in advance
5/2/2022, 5:54:03 PMHikconnect BridgeHikConnectERRORError: HikConnect needs account and password specified in config
5/2/2022, 5:54:03 PMHikconnect BridgeBridge is running on port 51846.
5/2/2022, 6:07:02 PMHikconnect Bridge stopping
5/2/2022, 6:07:02 PMHikconnect BridgeERRORdnsmasq: no process found
5/2/2022, 6:07:04 PMHikconnect BridgeERRORdnsmasq: no process found
5/2/2022, 6:30:16 PMbroadlinkrmprobridge stopping
5/2/2022, 6:30:22 PMBroadlink Rm Pro Bridge starting
5/2/2022, 6:38:22 PMeWeLink Bridge stopping
5/2/2022, 6:38:22 PMBroadlink Rm Pro Bridge stopping
5/2/2022, 6:38:32 PMWARNINGservice restart command received
5/2/2022, 6:38:43 PMhub is running on port 80
5/2/2022, 6:45:34 PMWARNINGdevice shutdown command received
5/2/2022, 6:54:27 PMhub is running on port 80
5/2/2022, 7:06:20 PMHikconnect Bridge starting
5/2/2022, 7:06:41 PMHikconnect BridgeLoaded plugin 'homebridge-hikconnect'
5/2/2022, 7:06:41 PMHikconnect BridgeLoading 1 platforms...
5/2/2022, 7:06:41 PMHikconnect BridgeHikConnectERRORError: HikConnect needs account and password specified in config
5/2/2022, 7:06:42 PMHikconnect BridgeBridge is running on port 51826.

Hello,
If you are using homebridge UI then username and password should pop up when you install the plugin.
If not then you must edit the config.json file in your homebridge directory and extend the platforms section like this:
"platforms": [
  {
    "account": "<your account name>",
    "password": "<your password>",
    "platform": "HikConnect"
  }
]

Thank you for your response

If you are using homebridge UI then username and password should pop up when you install the plugin.

Unfortunately I am using the web and app UI, but neither of them did pop up, asking for username and password.
I tried to factory reset my Hoobs, but did not work.

Ah so you are using Hoobs. I'm not familiar with it but I think you should able to edit the configuration directly on the web UI: https://support.hoobs.org/docs/60e0d060646faaa152f10dda (check the advanced editor part)


Create secrets for serviceaccounts

K8s 1.24+ doesn't autocreate secret for serviceaccount. This
breaks submariner deployments which expects secret to be created
and associated to the corresponding serviceaccount.
The fix ensures the Secret of type service-account-token exists,
is annotated with ServiceAccount.Name, and is added to Secrets list in
the ServiceAccount.
Signed-off-by: Vishal Thapar<EMAIL_ADDRESS>

@tpantelis ^^


add NCBITaxon: Borrelia miyamotoi (NCBITaxon: 47466)

In the last import, Borrelia miyamotoi (NCBITaxon:47466) was added in ncbitaxon_terms.txt, but it was not added to "merged_import.owl", and therefore it is not available to use for axiom annotations.
I don't know what is special about this NCBITaxon.
Please add this NCBITaxon to Mondo.
Follow-up work (for Sabrina): add axiom annotation to MONDO:0958150 based on PMID:36113496 and PMID:36839539

Was there any kind of logging output from the sh run.sh make refresh-merged command?

Was there any kind of logging output from the sh run.sh make refresh-merged command?
I don't think so (but maybe I don't understand the question).

It looks like the issue is that NCBITaxon:87882 needs to be added to https://github.com/obophenotype/ncbitaxon/blob/master/subsets/taxon-subset-ids.txt?
I'll do that!
Thanks!

Ok, so the new updates to the docs along with the PR being merged in https://github.com/obophenotype/ncbitaxon/blob/master/subsets/taxon-subset-ids.txt resolved the issue?


Creating a PEM file containing both cert and private key?

Is there a way to use the ssl_certificate cookbook to create a PEM file containing the signed certificate, intermediate certs (if any), and private key, in that order?  I'm trying to write a cookbook to configure some HAProxy instances, and HAProxy needs the certs and key to live in a PEM file together.  We already use ssl_certificate to manage SSL certs for other applications and generate self-signed certs in TestKitchen, so I was hoping to use it for HAProxy too.

No, currently. Although I do not think that it will be difficult to add it to the cookbook.
But I have some doubts about how we can implement the interface. How do you expect it to work? I mean, adding a property to the resource and generating only the pem file, generating both the cert&key and pem, ...?
Of course, please, send me a PR if you try to implement it.

I'm not sure of how it should be implemented, but here are a couple thoughts:

new resource(s) that takes existing certs/keys as input properties and converts them (e.g. pkcs12 to pem or vice-versa) and/or adds them to new or existing .pem or .p12 container files
more properties on the existing resource for controlling whether the cert/key are output as one .pem file containing all certs/keys or output as one file per cert/key

The certificate cookbook has the boolean properties combined_file and nginx_cert to achieve this.


Fix sanity check specs warnings

Fix build errors on sanity check specs.

Bundler.with_clean_env is deprecated
Avoid warning with open-ended dependency

WARNING:  open-ended dependency on activesupport (>= 4.2) is not recommended
  if activesupport is semantically versioned, use:
    add_runtime_dependency 'activesupport', '~> 4.2'
WARNING:  open-ended dependency on actionpack (>= 4.2) is not recommended
  if actionpack is semantically versioned, use:
    add_runtime_dependency 'actionpack', '~> 4.2'
WARNING:  open-ended dependency on railties (>= 4.2) is not recommended
  if railties is semantically versioned, use:
    add_runtime_dependency 'railties', '~> 4.2'
WARNING:  See http://guides.rubygems.org/specification-reference/ for help

It seems there is an issue with the last Bundler. I downgrade to 2.0.2 on my machine and I don't have the error anymore.

Related issue for bundler https://github.com/bundler/bundler/issues/7487

Sorry for the noise. I will look at JRuby errors.

Just a heads-up regarding Overwrite the executable? [yN] No output has been received in the last 10m0s - will try to fix it here https://github.com/rspec/rspec-rails/pull/2234

@pirj I don't think we need to change this part anymore. We still have this kind of noise.
Verify required rspec dependencies
WARNING:  pessimistic dependency on rake (~> 10.0.0, development) may be overly strict
  if rake is semantically versioned, use:
    add_development_dependency 'rake', '~> 10.0', '>= 10.0.0'
WARNING:  pessimistic dependency on thread_order (~> 1.1.0, development) may be overly strict
  if thread_order is semantically versioned, use:
    add_development_dependency 'thread_order', '~> 1.1', '>= 1.1.0'
WARNING:  See http://guides.rubygems.org/specification-reference/ for help
WARNING:  pessimistic dependency on rake (~> 10.0.0, development) may be overly strict
  if rake is semantically versioned, use:
    add_development_dependency 'rake', '~> 10.0', '>= 10.0.0'
WARNING:  pessimistic dependency on cucumber (~> 1.3.15, development) may be overly strict
  if cucumber is semantically versioned, use:
    add_development_dependency 'cucumber', '~> 1.3', '>= 1.3.15'
WARNING:  See http://guides.rubygems.org/specification-reference/ for help
WARNING:  pessimistic dependency on rake (~> 10.0.0, development) may be overly strict
  if rake is semantically versioned, use:
    add_development_dependency 'rake', '~> 10.0', '>= 10.0.0'
WARNING:  See http://guides.rubygems.org/specification-reference/ for help
WARNING:  See http://guides.rubygems.org/specification-reference/ for help
WARNING:  pessimistic dependency on rake (~> 10.0.0, development) may be overly strict
  if rake is semantically versioned, use:
    add_development_dependency 'rake', '~> 10.0', '>= 10.0.0'
WARNING:  pessimistic dependency on thread_order (~> 1.1.0, development) may be overly strict
  if thread_order is semantically versioned, use:
    add_development_dependency 'thread_order', '~> 1.1', '>= 1.1.0'
WARNING:  See http://guides.rubygems.org/specification-reference/ for help
WARNING:  See http://guides.rubygems.org/specification-reference/ for help
WARNING:  pessimistic dependency on rake (~> 10.0.0, development) may be overly strict
  if rake is semantically versioned, use:
    add_development_dependency 'rake', '~> 10.0', '>= 10.0.0'
WARNING:  See http://guides.rubygems.org/specification-reference/ for help
WARNING:  pessimistic dependency on rake (~> 10.0.0, development) may be overly strict
  if rake is semantically versioned, use:
    add_development_dependency 'rake', '~> 10.0', '>= 10.0.0'
WARNING:  pessimistic dependency on cucumber (~> 1.3.15, development) may be overly strict
  if cucumber is semantically versioned, use:
    add_development_dependency 'cucumber', '~> 1.3', '>= 1.3.15'
WARNING:  See http://guides.rubygems.org/specification-reference/ for help
  passes when libraries are required (FAILED - 1)

Failures:

  1) Verify required rspec dependencies passes when libraries are required
     Failure/Error: expect(`bundle exec #{script} 2>&1`).to be_empty
       expected `"WARNING:  pessimistic dependency on rake (~> 10.0.0, development) may be overly strict\n  if rake is...'~> 1.3', '>= 1.3.15'\nWARNING:  See http://guides.rubygems.org/specification-reference/ for help\n".empty?` to return true, got false

With
--- a/spec/sanity_check_spec.rb
+++ b/spec/sanity_check_spec.rb
@@ -47,6 +47,7 @@ RSpec.describe "Verify required rspec dependencies" do
     FileUtils.chmod 0777, script

     bundler_env do
+      puts `bundle exec #{script} 2>&1`
       expect(`bundle exec #{script} 2>&1`).to be_empty
       expect($?.exitstatus).to eq(0)
     end

But where are those dependencies? It seems this noise come from other gemspec of dependencies of gems we install. If think we should rewrite this test.

@benoittgt I meant that there are build jobs that stuck and fail with timeout while trying to read user input from console on build setup stage. It currently fails on 2.6.3 and 2.3.8 builds. I'll debug it later today.

@benoittgt Can you try integrating this and see if the build is green?

I cherry-picked my suggested version of this to another PR, #2240 and merged it because I'm working on fixing the other builds, but I think this is getting closer to fixing rails too.

This is so broken.
Installing RubyGems 3.1.1
  Successfully built RubyGem
  Name: bundler
  Version: 2.1.0
  File: bundler-2.1.0.gem
ERROR:  While executing gem ... (Gem::InstallError)
    "bundle" from bundler conflicts with /home/travis/.rvm/rubies/ruby-2.6.3/bin/bundle

🤦🏼♂
We have the again the issue where it seems we have bundler 2.1.0 and bundler 2.1.1 installed and the default bundler is 2.1.0?
I am not able to reproduce this error locally.
** Execute cucumber

/home/travis/.rvm/rubies/ruby-2.6.3/bin/ruby -S bundle exec cucumber --tag<EMAIL_ADDRESS>--tag ~@rails_pre_5 --tag ~@rails_post_6

(....)

+/home/travis/.rvm/gems/ruby-2.6.3/gems/bundler-2.1.1/lib/bundler/runtime.rb:312:in `check_for_activated_spec!': You have already activated bundler 2.1.0, but your Gemfile requires bundler 2.1.1. Since bundler is a default gem, you can either remove your dependency on it or try updating to a newer version of bundler that supports bundler as a default gem. (Gem::LoadError)

Locally I have:
$ gem list | grep '^bundler ('
bundler (2.1.1, 2.1.0, 2.0.2)
$ bundle exec rake cucumber
/Users/bti/.rvm/rubies/ruby-2.6.3/bin/ruby -S bundle exec cucumber --tag<EMAIL_ADDRESS>--tag ~@rails_pre_5 --tag ~@rails_pre_6

Using the default profile...
................... (no errors)

It's worst than before.
Good output:
Fetching bundler-2.1.1.gem
Successfully installed bundler-2.1.1 as a default gem
Done installing documentation for bundler after 0 seconds
1 gem installed
Current bundler versions installed after 'gem install bundler': bundler (default: 2.1.1, default: 2.1.0)

But after
$ bundle install --binstubs --path ../bundle --retry=3 --jobs=3
Traceback (most recent call last):
	1: from /home/travis/.rvm/gems/ruby-2.6.3/bin/bundle:23:in `<main>'
/home/travis/.rvm/gems/ruby-2.6.3/bin/bundle:23:in `load': cannot load such file -- /home/travis/.rvm/rubies/ruby-2.6.3/lib/ruby/gems/2.6.0/gems/bundler-2.1.1/exe/bundle (LoadError)

The command "eval bundle install --binstubs --path ../bundle --retry=3 --jobs=3 " failed. Retrying, 2 of 3.

I have to move and could probably look at it, but not until tomorrow. Feel free to remove my commits.

Hey @mvz, can you please take a look?
We're experiencing a weird issue:
You have already activated bundler 2.1.0, but your Gemfile requires bundler 2.1.1. Since bundler is a default gem, you can either remove your dependency on it or try updating to a newer version of bundler that supports bundler as a default gem. (Gem::LoadError)
from /home/travis/.rvm/gems/ruby-2.6.3/gems/bundler-2.1.1/lib/bundler/runtime.rb:31:in `block in setup'
from /home/travis/.rvm/gems/ruby-2.6.3/gems/bundler-2.1.1/lib/bundler/spec_set.rb:147:in `each'
from /home/travis/.rvm/gems/ruby-2.6.3/gems/bundler-2.1.1/lib/bundler/spec_set.rb:147:in `each'
from /home/travis/.rvm/gems/ruby-2.6.3/gems/bundler-2.1.1/lib/bundler/runtime.rb:26:in `map'
from /home/travis/.rvm/gems/ruby-2.6.3/gems/bundler-2.1.1/lib/bundler/runtime.rb:26:in `setup'
from /home/travis/.rvm/gems/ruby-2.6.3/gems/bundler-2.1.1/lib/bundler.rb:149:in `setup'
from /home/travis/.rvm/gems/ruby-2.6.3/gems/bundler-2.1.1/lib/bundler/setup.rb:20:in `block in <top (required)>'
from /home/travis/.rvm/gems/ruby-2.6.3/gems/bundler-2.1.1/lib/bundler/ui/shell.rb:136:in `with_level'
from /home/travis/.rvm/gems/ruby-2.6.3/gems/bundler-2.1.1/lib/bundler/ui/shell.rb:88:in `silence'
from /home/travis/.rvm/gems/ruby-2.6.3/gems/bundler-2.1.1/lib/bundler/setup.rb:20:in `<top (required)>'
from /home/travis/.rvm/rubies/ruby-2.6.3/lib/ruby/site_ruby/2.6.0/rubygems/core_ext/kernel_require.rb:92:in `require'
from /home/travis/.rvm/rubies/ruby-2.6.3/lib/ruby/site_ruby/2.6.0/rubygems/core_ext/kernel_require.rb:92:in `require'
from /home/travis/build/rspec/rspec-rails/tmp/aruba/bin/rspec:27:in `<main>'

After reverting aruba update commit, this problem disappears.

That is strange. @pirj any idea how to reproduce this locally?

I think what happens is that bundler now sets more env vars than aruba knows to unset. I'll be trying out some fixes in #2241.

Thanks for the quick response!

@mvz Jon asked the question about ENV vars but without clear answer https://github.com/bundler/bundler/issues/7487#issuecomment-567252536
I am trying at the moment to look at difference between 2.0 and 2.1 but the diff is big. https://github.com/bundler/bundler/compare/2-0-stable...2-1-stable

Congrats @pirj that's much better!

@pirj it seems the latest commit in #2241 (c024004970a46eeeb4ff090f00e9f250c7c1241b) fixed the bundler version problem.

Edit 2: Error on jruby seems related to all commands linked to gem.

If you're able to update the JSON gem to 2.3.0, it should resolve that problem.  They stopped using the deprecated rubyforge_project attribute in their gemspec with that version, and that's the source of the warning.

@duckinator but even if I update json gem and set JSON gem 2.3.0 as default. I still have the warning.

Closing, in favor of https://github.com/rspec/rspec-rails/pull/2244

Thanks for all your work here @pirj @benoittgt and @mvz I tried to cherry-pick your commits for #2244 where possible, I just wanted the minimal changes to fix the build.

I am happy with the final result. I think we should open a new discussion on how the build is written. I would love to be able to more easily run the build locally, and run each step without looking at shell functions or Rakefile. Like simply copy paste one line from travis logs.

@benoittgt more than happy for you to look at that :)


Fix JSHint errors

Signed-off-by: Shatakshi<EMAIL_ADDRESS>Description
This PR fixes some JSHint errors as reported by Sonatype Lift.
Notes for Reviewers
Signed commits

[x] Yes, I signed my commits.

Codecov Report

Merging #4424 (3d7e3df) into master (d943ac3) will decrease coverage by 7.70%.
The diff coverage is n/a.

@@            Coverage Diff             @@
##           master    #4424      +/-   ##
==========================================
- Coverage   31.85%   24.15%   -7.71%
==========================================
  Files          59       59
  Lines        5063     5063
==========================================
- Hits         1613     1223     -390
- Misses       2989     3501     +512
+ Partials      461      339     -122

Flag
Coverage Δ

gointegrationtests
?

unittests
24.15% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files
Coverage Δ

mesheryctl/internal/cli/root/system/status.go
0.00% <0.00%> (-62.83%)
:arrow_down:

mesheryctl/internal/cli/root/system/start.go
0.99% <0.00%> (-53.98%)
:arrow_down:

mesheryctl/internal/cli/root/system/restart.go
2.50% <0.00%> (-47.50%)
:arrow_down:

mesheryctl/internal/cli/root/system/stop.go
0.85% <0.00%> (-36.76%)
:arrow_down:

mesheryctl/internal/cli/root/system/update.go
1.05% <0.00%> (-34.74%)
:arrow_down:

mesheryctl/internal/cli/root/system/check.go
1.39% <0.00%> (-22.38%)
:arrow_down:

mesheryctl/internal/cli/root/system/logs.go
3.29% <0.00%> (-20.88%)
:arrow_down:

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0264776...3d7e3df. Read the comment docs.

Any progress here? @Shatakshi0805

hi, i think i have made requested changes. if there's something else that needs to be done please let me know.

hi, i think i have made requested changes. if there's something else that needs to be done please let me know.

Yep, this is good enough. I think should be good to merge here! @leecalcote @navendu-pottekkat


Style change on edit (box-shadow and border padding)

I added some style to the editor and i noted a problem on some of them.
ContentTools.StylePalette.add([`
       new ContentTools.Style('inset shadow', 'img-shadow-inset', ['img']),
       new ContentTools.Style('external border', 'img-border-ext', ['img'])
          `]);

.img-border-ext {
  border: solid ;
  border-width: 1px;
  padding: 3px;
}

.img-shadow-inset {
  -webkit-box-shadow: inset 70px 70px 20px -7px rgba(0,0,0,0.90);
  -moz-box-shadow: inset 70px 70px 20px -7px rgba(0,0,0,0.90);
  box-shadow: inset 70px 70px 20px -7px rgba(0,0,0,0.90);
}

The class containing a border (img-border-ext) is not displayed correctly during editing.
But the strangest thing is the another class, with an inset box-shadow, that is only displayed correctly during editing.
In other words, the page changes when the edit start.
You could view this problem here:
https://jsfiddle.net/draio/yk3tvjpg/
Thanks
UPDATE:
Ok, my mistake ... The CSS3 inset shadows don’t work on images
On edit mode the image is "transformed" into a div and thats do the trick.

Hi @draio,
The reason for the box shadow I imagine is because images many support inset, however when in edit mode images are converted to divs for the purpose of interaction and therefore the style is supported.
For the same reason your border appears differently in the edit mode because your applying padding which is handled differently by images than it is by divs. Setting the size of the border to 4px and removing the padding from the image is all that's required to resolve the issue.

Thank you!

Just to let you know ... or for someone else who has the same problem.
The .img-border-ext class show a single 1 pixel border to 3 pixels from the image.
Removing the padding and changing the width make only a big border (4px).
I added "background-clip: content-box" that move the background to the content area of the div .
So now I have a class that works both with img and div ... and seems almost equal in normal or edit mode. It's not perfect but it works.
.img-border-ext {
  border: solid ;
  border-width: 1px;
  padding: 3px;
  background-clip: content-box;
}


Update image docker.io/getmeili/meilisearch to v1.10

This PR contains the following updates:

Package
Update
Change

docker.io/getmeili/meilisearch
minor
v1.9 -> v1.10

Configuration
📅 Schedule: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.

[ ] If you want to rebase/retry this PR, check this box

This PR has been generated by Renovate Bot.

Edited/Blocked Notification
Renovate will not automatically rebase this PR, because it does not recognize the last commit author and assumes somebody else may have edited the PR.
You can manually request rebase by checking the rebase/retry box above.
⚠ Warning: custom changes will be lost.


style(main):  property is sorted by the A-Z

I have code cleanliness.
When I read the webpack source code, I found webpack main some attributes are not sorted according to A-Z, and most of the source code is sorted according to A-Z, so I submit a pull request.
I think this pull request improves the readability of the code
What kind of change does this PR introduce?

a code style

Did you add tests for your changes?

no, just code style

Does this PR introduce a breaking change?

no

What needs to be documented once your changes are merged?

do not need

pass test & pass lint


Fix for [HHH-8181]

This fix uses suppressed throwables to make sure that any exception thrown within the try clause of a managed flush is sent to the client even if there is another exception in the finally clause. The runtime exception from within the finally clause is added as a suppressed exception to the exception originating from the try clause.

I think this was one of the PRs that got closed accidentally.