Deprecation Cycles

138 views
Skip to first unread message

Aaron Meurer

unread,
Jan 25, 2012, 3:36:51 PM1/25/12
to sy...@googlegroups.com
Hi.

We've discussed this a little before. We currently are deprecating a
lot of things, which I personally see as a good thing, because it
means that we are cleaning up some old cruft. I would like to start
some discussion about it, though, particularly about the length of
deprecation cycles. I'd like to hear both from developers and from
users.

- First off, I think we can all agree that it's a good thing to set
deprecation cycles specifically, so that users will know exactly when
they can expect the old feature to be removed. My questions below
rely on this assumption.

- Do you think we should have a set deprecation cycle length, or
should we decide each on a per-case basis? We could also set
different lengths for different situations, e.g., if the feature
change would likely cause severe problems in many people's code, we
could make the cycle longer. On the other hand, if some now feature is
blocked on removing deprecated behavior, we could make it shorter.

Even if you think the answer to the above question is "per-case", the
rest of these are still worth discussing. But if you think they
should be set, what should they be set to?

- Should deprecation cycles be time based or version based? On the
one hand, our release cycle is irregular, and will remain so for the
foreseeable future. We went over a year between 0.6.7 and 0.7.0.
Then we went just over a month to release 0.7.1. So there may be
something to be said for having time based cycles instead of release
based ones ("the next release after February 1, 2013" or something
like that). On the other hand, there's no telling how SymPy itself
will advance in a given period of time (I guess the same can be true
about releases themselves, but there's at least some guarantee of a
certain amount of changes for every 0.0.1 or 0.1 release).

I think no release will be under a month, unless we find some minor
problem with a release immediately after it goes out. In that case, I
think we should do a 0.0.0.1 release (like 0.7.2.1 for example).

So I think that if we do release based deprecations, we should set a
specific release, not "+2 releases" or something like that, to handle
potential 0.0.0.1 releases. The question here is what should we do
about 0.1 releases? I don't think these should happen very often,
only when we have a very significant change in the codebase, (e.g.,
for 0.7.0 we had the new polys). But if we say "this feature will be
removed by 0.7.4" and the next release after 0.7.3 is 0.8.0, then that
could cause confusion. We could be precise and say "the next two
0.0.1 or 0.1 releases", but that starts to get confusing fast.

- I discussed the implementation of this in another thread, and I
don't think there were any disagreements. The idea was to add flags
to SymPyDeprecationWarning (and to @deprecated, which would pass them
through), which would build up a deprecation string with the relevant
information. We would also do necessary tracking in the issues to
make sure old features are removed when they should be.

Here are two examples of things we deprecated or plan to deprecate, so
you can get an idea of the sorts of things I'm talking about:

- In 0.7.0, we renamed Real to Float. We kept Real and .is_Real as
deprecated shortcuts to Float and .is_Float, respectively. This is a
good example of a fairly harmless deprecation. Keeping the old names
around indefinitely would not harm anything, other than that they
clutter things up. Furthermore, if they were removed, it would be
trivial for users to fix their code, and, perhaps more importantly, it
would be easy for them to tell that they would need to do that,
because they would get ImportError or AttributeError, which would
clearly tell them that Real was not defined any more. A simple Google
search or question here would reveal that it's been renamed to Float.

- In https://github.com/sympy/sympy/pull/1009, we are changing the
syntax for Piecewise. The current syntax is Piecewise((expr1, cond1),
(expr2, cond2), ..., (exprn, condn), (otherwise, True)). For example,
Piecewise((1, x < 0), (2, x > 0), (0, True)) means 1 if x is negative,
2 if x is positive, and 0 otherwise.

However, the use of True to signify the otherwise condition is
problematic, because a user may enter, either manually or
programmatically, a condition that automatically evaluated to True.
So something like Piecewise((1, x < 0), (2, 1 > 0)) would evaluate to
a Piecewise, not 2 as would be expected.

The solution is to change the syntax to Piecewise((expr1, cond1), ...,
(exprn, condn), otherwise), where the otherwise condition is optional.
With the new syntax, if any condition is True, the piecewise will
automatically return the corresponding expression. The above would
become simply Piecewise((1, x < 0), (2, x > 0), 0)

For now, we are supporting both syntaxes in the following way: if a
condition that is not the last condition it True, or if otherwise is
given using the new syntax and any condition is True, the
corresponding expression is returned. If the last condition is True,
and no otherwise is given (i.e., the old syntax), the corresponding
expression is set to the otherwise condition and a deprecation warning
is raised.

This deprecation is a whole different animal. On the one hand, as
long as we support both the old syntax and the new syntax
simultaneously, it is very confusing, inconsistant, and essentially
wrong. On the other, changing it permanently, so that Piecewise((1, x
< 0), (2, x > 0), (0, True)) returns 0 instead of a Piecewise with an
otherwise condition, would be much harder on users. It could
potentially go unnoticed, especially if the Piecewise in question
would have evaluated to the otherwise condition anyway, and would
either raise no error and just return a wrong result, or would raise
some cryptic error (e.g., "IndexError: tuple index out of range" if
they tried to call .args).

Finally, I want to point out that users can avoid ambiguity during the
deprecation cycle by manually passing nan as the otherwise condition
(like Piecewise((1, x < 0), (2, x > 0), (0, True), nan)). nan is the
default otherwise condition, so this effectively does nothing but
force the new syntax and silence the deprecation warning.

Aaron Meurer

Joachim Durchholz

unread,
Jan 25, 2012, 6:05:33 PM1/25/12
to sy...@googlegroups.com
Am 25.01.2012 21:36, schrieb Aaron Meurer:
> - Do you think we should have a set deprecation cycle length, or
> should we decide each on a per-case basis? We could also set
> different lengths for different situations, e.g., if the feature
> change would likely cause severe problems in many people's code, we
> could make the cycle longer. On the other hand, if some now feature is
> blocked on removing deprecated behavior, we could make it shorter.

It would be best if new stuff can be added without removing deprecated
stuff.
If it's really incompatible, make things live in different modules (but
still make sure that they interoperate).

I know this isn't always possible, but one can design for it.
E.g. by having pluggable instead of hardcoded heuristics, for example.

Removing features, even deprecated ones, is always bad.

> Even if you think the answer to the above question is "per-case", the
> rest of these are still worth discussing. But if you think they
> should be set, what should they be set to?

If things are designed so that deprecated stuff can live in parallel
with new stuff, the deprecation cycle could be "infinite".

Deprecation would then be just a warning to people that other, better
implementations are available elsewhere.
(Deprecation markers should also contain or point to clear instructions
how to get rid of calls to old cruft.)

> - Should deprecation cycles be time based or version based?

Since deprecation is a service to SymPy users, and users don't know what
the development speed and version timing will be, they'll want a
time-based deprecation.

If you mean "period from deprecation warning to feature removal", then
it should definitely be time-based. Imagine somebody writing a PhD
thesis, using a feature, finding it deprecated, and not knowing whether
it will go away a day before he finishes or a day after, since he
doesn't know when the release will come.

Of course, people can still stick with an old version of SymPy, but not
everybody can do that (campus policy may ban old software versions, for
example, and then it's too bad if your thesis gets derailed...)

> On the
> one hand, our release cycle is irregular, and will remain so for the
> foreseeable future. We went over a year between 0.6.7 and 0.7.0.
> Then we went just over a month to release 0.7.1. So there may be
> something to be said for having time based cycles instead of release
> based ones ("the next release after February 1, 2013" or something
> like that). On the other hand, there's no telling how SymPy itself
> will advance in a given period of time (I guess the same can be true
> about releases themselves, but there's at least some guarantee of a
> certain amount of changes for every 0.0.1 or 0.1 release).

It's essentially impossible to give a date.

> I think no release will be under a month, unless we find some minor
> problem with a release immediately after it goes out. In that case, I
> think we should do a 0.0.0.1 release (like 0.7.2.1 for example).

I think bug fixes should always be possible.

> Here are two examples of things we deprecated or plan to deprecate, so
> you can get an idea of the sorts of things I'm talking about:
>
> - In 0.7.0, we renamed Real to Float. We kept Real and .is_Real as
> deprecated shortcuts to Float and .is_Float, respectively. This is a
> good example of a fairly harmless deprecation. Keeping the old names
> around indefinitely would not harm anything, other than that they
> clutter things up. Furthermore, if they were removed, it would be
> trivial for users to fix their code, and, perhaps more importantly, it
> would be easy for them to tell that they would need to do that,
> because they would get ImportError or AttributeError, which would
> clearly tell them that Real was not defined any more. A simple Google
> search or question here would reveal that it's been renamed to Float.

A better migration path would be to keep Real and .is_Real around, but
let them throw exceptions.
That way, people can be given exact instructions what they need to change.

An ImportError or AttributeError tells you what's wrong, but it does not
tell you how to fix it.

Alternatively, we could make a "deprecated SymPy" package that has all
the old clutter in it, with deprecation warnings and docs and stuff
helping people migrate.

Variant 1: Name the new Piecewise something different, say, Piecewise2.

Variant 2: Put the different Piecewise classes in different, mutually
incompatible modules and instruct people to import one or the other, but
not both.
E.g. the module structure could look like this:
sympy. - current API
sympy.api1 - API 1 (deprecated)
sympy.api2 - API 2 (deprecated, too)
...
sympy.api10 - API 10 (last API we deprecated)
sympy.api11 - API 11 (current)

Modules in sympy will just load modules from api11.

People who need long-term stability import their modules from sympy.apiNN.
People who want to just hack something up import their modules from sympy.

(I hope this is understandable.)

> Finally, I want to point out that users can avoid ambiguity during the
> deprecation cycle by manually passing nan as the otherwise condition
> (like Piecewise((1, x< 0), (2, x> 0), (0, True), nan)). nan is the
> default otherwise condition, so this effectively does nothing but
> force the new syntax and silence the deprecation warning.

That's not a systematic way to invoke an old API.
It would work for the Piecewise use case I guess, but it wouldn't work
in general.

Just my 2c.

Regards,
Jo

Ronan Lamy

unread,
Jan 25, 2012, 7:15:59 PM1/25/12
to sy...@googlegroups.com
Le mercredi 25 janvier 2012 à 13:36 -0700, Aaron Meurer a écrit :

> - Should deprecation cycles be time based or version based? On the

Version-based deprecations are more convenient for users and developers,
I think, but we'd need to set a release policy - define the difference
between major and minor versions, and find a more constraining answer to
"When do we release?" than "Whenever we feel like it."

> - In https://github.com/sympy/sympy/pull/1009, we are changing the
> syntax for Piecewise.

In this case, it would be better to create a new object with a new name,
considering that Piecewise isn't a good name (it's supposed to be
piecewise-what, exactly?) and that the object is actually a
case-expression. It would also allow to put its args in the right order,
with the results after the corresponding tests.


Ronan Lamy

unread,
Jan 25, 2012, 7:58:49 PM1/25/12
to sy...@googlegroups.com
Le jeudi 26 janvier 2012 à 00:05 +0100, Joachim Durchholz a écrit :
> Am 25.01.2012 21:36, schrieb Aaron Meurer:

> > Even if you think the answer to the above question is "per-case", the
> > rest of these are still worth discussing. But if you think they
> > should be set, what should they be set to?
>
> If things are designed so that deprecated stuff can live in parallel
> with new stuff, the deprecation cycle could be "infinite".
>
> Deprecation would then be just a warning to people that other, better
> implementations are available elsewhere.
> (Deprecation markers should also contain or point to clear instructions
> how to get rid of calls to old cruft.)

If we don't want to eventually remove some feature, we shouldn't
deprecate it in the first place.

Keeping old stuff has a significant cost in maintenance and in
development agility. As long as we keep it, it must stay in working
condition and there are some changes that are simply impossible (for
instance, creating a new object with the same name as some deprecated
thing).


>
> > - Should deprecation cycles be time based or version based?
>
> Since deprecation is a service to SymPy users, and users don't know what
> the development speed and version timing will be, they'll want a
> time-based deprecation.
>
> If you mean "period from deprecation warning to feature removal", then
> it should definitely be time-based. Imagine somebody writing a PhD
> thesis, using a feature, finding it deprecated, and not knowing whether
> it will go away a day before he finishes or a day after, since he
> doesn't know when the release will come.
>

Your user doesn't know anything either if deprecation is time-based.
Since we aren't going to release a time-bomb inside the library, the
deprecation means that it'll be removed in the next version after the
cut-off date, but it can land on the user's computer anywhere between 3
months and 5 years after said date...

> Of course, people can still stick with an old version of SymPy, but not
> everybody can do that (campus policy may ban old software versions, for
> example, and then it's too bad if your thesis gets derailed...)

> > - In https://github.com/sympy/sympy/pull/1009, we are changing the

Urgh!! That would mean we'd have to maintain 11 different APIs instead
of only one. That would be a nightmare.


Joachim Durchholz

unread,
Jan 26, 2012, 2:51:10 AM1/26/12
to sy...@googlegroups.com
Am 26.01.2012 01:58, schrieb Ronan Lamy:
> Le jeudi 26 janvier 2012 � 00:05 +0100, Joachim Durchholz a �crit :

>> Am 25.01.2012 21:36, schrieb Aaron Meurer:
>
>>> Even if you think the answer to the above question is "per-case", the
>>> rest of these are still worth discussing. But if you think they
>>> should be set, what should they be set to?
>>
>> If things are designed so that deprecated stuff can live in parallel
>> with new stuff, the deprecation cycle could be "infinite".
>>
>> Deprecation would then be just a warning to people that other, better
>> implementations are available elsewhere.
>> (Deprecation markers should also contain or point to clear instructions
>> how to get rid of calls to old cruft.)
>
> If we don't want to eventually remove some feature, we shouldn't
> deprecate it in the first place.

Deprecation still serves as notice to users:
- If there's a problem, there won't be fixes
- There's a newer and better API available

> Keeping old stuff has a significant cost in maintenance and in
> development agility.

I know. Sometimes stuff simply has to go.
Removing stuff should be avoided though.

Consider Linux. The userspace interface has been extremely stable -
stuff was added, but never removed, unless it was really impossible or
didn't make sense.
That's what enables people to use 10-year-old Linux software, with no
changes. (Compare that with Windows - trying to run decade-old
applications is an exercise in frustration.)

*Internal* interfaces in Linux, however, are ruthlessly restructured at
the drop of a hat.

That's how Linux stays agile despite being highly compatible.

> As long as we keep it, it must stay in working
> condition and there are some changes that are simply impossible (for
> instance, creating a new object with the same name as some deprecated
> thing).
>>
>>> - Should deprecation cycles be time based or version based?
>>
>> Since deprecation is a service to SymPy users, and users don't know what
>> the development speed and version timing will be, they'll want a
>> time-based deprecation.
>>
>> If you mean "period from deprecation warning to feature removal", then
>> it should definitely be time-based. Imagine somebody writing a PhD
>> thesis, using a feature, finding it deprecated, and not knowing whether
>> it will go away a day before he finishes or a day after, since he
>> doesn't know when the release will come.
>>
> Your user doesn't know anything either if deprecation is time-based.
> Since we aren't going to release a time-bomb inside the library, the
> deprecation means that it'll be removed in the next version after the
> cut-off date, but it can land on the user's computer anywhere between 3
> months and 5 years after said date...

Yes, but they can plan ahead. In the sense that "I'll need a year to
complete this, but feature Foo may be unavailable in five months from
now, so I better restructure my code now to use the other feature".

If removal is release-based, a feature could get deprecated tomorrow and
removed four weeks after that (assuming a minimum distance of four weeks).


Essentially, the question is:
"What minimum guarantees do we give SymPy users about the long-term
stability?"
Release-based deprecation would mean: features are guaranteed to stay
available for the next four weeks.
Time-based deprecation would mean: features are guaranteed to stay for
whatever perdiod we define.

>> Variant 2: Put the different Piecewise classes in different, mutually
>> incompatible modules and instruct people to import one or the other, but
>> not both.
>> E.g. the module structure could look like this:
>> sympy. - current API
>> sympy.api1 - API 1 (deprecated)
>> sympy.api2 - API 2 (deprecated, too)
>> ...
>> sympy.api10 - API 10 (last API we deprecated)
>> sympy.api11 - API 11 (current)
>>
>> Modules in sympy will just load modules from api11.
>>
>> People who need long-term stability import their modules from sympy.apiNN.
>> People who want to just hack something up import their modules from sympy.
>
> Urgh!! That would mean we'd have to maintain 11 different APIs instead
> of only one. That would be a nightmare.

Old APIs don't need much maintenance. They don't even need to
interoperate with the newer APIs (unless we want to make them do that).

Think of them as old versions of SymPy, packaged as part of the whole
system.

The alternative might be keeping old versions of SymPy around for download.

Brian Granger

unread,
Jan 26, 2012, 12:33:03 PM1/26/12
to sy...@googlegroups.com
I am -1 on establishing formal deprecation cycles or multiple APIs.
For an open source project with limited manpower, these just hinder
progress and confuse users. We are better off making good decisions,
documenting them and moving forward quickly.

Cheers,

Brian

> --
> You received this message because you are subscribed to the Google Groups "sympy" group.
> To post to this group, send email to sy...@googlegroups.com.
> To unsubscribe from this group, send email to sympy+un...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/sympy?hl=en.
>

--
Brian E. Granger
Cal Poly State University, San Luis Obispo
bgra...@calpoly.edu and elli...@gmail.com

Aaron Meurer

unread,
Jan 26, 2012, 12:32:50 PM1/26/12
to sy...@googlegroups.com
On Wed, Jan 25, 2012 at 4:05 PM, Joachim Durchholz <j...@durchholz.org> wrote:
> Am 25.01.2012 21:36, schrieb Aaron Meurer:
>
>> - Do you think we should have a set deprecation cycle length, or
>> should we decide each on a per-case basis?  We could also set
>> different lengths for different situations, e.g., if the feature
>> change would likely cause severe problems in many people's code, we
>> could make the cycle longer. On the other hand, if some now feature is
>> blocked on removing deprecated behavior, we could make it shorter.
>
>
> It would be best if new stuff can be added without removing deprecated
> stuff.
> If it's really incompatible, make things live in different modules (but
> still make sure that they interoperate).

Yes, of course. But in some cases, deprecation simply can't be
avoided. The alternative is just changing it entirely, which isn't
ideal for the user. I'm talking specifically about these cases.

>
> I know this isn't always possible, but one can design for it.
> E.g. by having pluggable instead of hardcoded heuristics, for example.

Right. We want to always try to get the best API the first time
around, so we don't ever have to change it. But unfortunately, the
APIs the currently exist don't always follow this, and some of them
really need to be changed (like Piecewise).

>
> Removing features, even deprecated ones, is always bad.
>
>
>> Even if you think the answer to the above question is "per-case", the
>> rest of these are still worth discussing.  But if you think they
>> should be set, what should they be set to?
>
>
> If things are designed so that deprecated stuff can live in parallel with
> new stuff, the deprecation cycle could be "infinite".

I agree with Ronan. It isn't really a deprecation if we do that.

Interesting idea. So you would have something like this (up to a
change in version numbers or dates):

0.7.0: Real, is_Real deprecated. Use of them works but raises a
deprecation warning telling the user to use Float.

0.7.2: Real, is_Real are made to raise something like ValueError("Use
Float instead.") or AttributeError("Use is_Float instead").

0.7.3: Real, is_Real are removed completely.

>
> An ImportError or AttributeError tells you what's wrong, but it does not
> tell you how to fix it.
>
> Alternatively, we could make a "deprecated SymPy" package that has all the
> old clutter in it, with deprecation warnings and docs and stuff helping
> people migrate.

We already have such a thing. It's called the old versions of SymPy.
They are not actively maintained (except in the form of the newest
version of SymPy).

This sort of thing is unnecessary in this case, because we can
actually reasonably support both syntaxes at the same time. In some
cases, such as with the new plotting module, this is not possible, so
we are splitting the module.

>
> Variant 2: Put the different Piecewise classes in different, mutually
> incompatible modules and instruct people to import one or the other, but not
> both.
> E.g. the module structure could look like this:
> sympy. - current API
> sympy.api1 - API 1 (deprecated)
> sympy.api2 - API 2 (deprecated, too)
> ...
> sympy.api10 - API 10 (last API we deprecated)
> sympy.api11 - API 11 (current)
>
> Modules in sympy will just load modules from api11.
>
> People who need long-term stability import their modules from sympy.apiNN.
> People who want to just hack something up import their modules from sympy.

I don't like this idea at all. If you want an old API, use an old
version. If wer did this, we would not be able to support the old
APIs, nor would we want to (that would defeat the whole purpose of why
we deprecated the old API in the first place). So this is essentially
the same as having old versions, except we don't pretend that we
support them by shipping them with the current version.

>
> (I hope this is understandable.)
>
>
>> Finally, I want to point out that users can avoid ambiguity during the
>> deprecation cycle by manually passing nan as the otherwise condition
>> (like Piecewise((1, x<  0), (2, x>  0), (0, True), nan)).  nan is the
>> default otherwise condition, so this effectively does nothing but
>> force the new syntax and silence the deprecation warning.
>
>
> That's not a systematic way to invoke an old API.
> It would work for the Piecewise use case I guess, but it wouldn't work in
> general.

Right, these were just concrete examples. The new plotting module
could be a third example, where the new API is completely
incompatibile with the old API. For that, we are going to split it
into a new module, which will eventually replace the old module (I
forgot the exact sequence we agreed one; maybe someone could remind
me).

Aaron Meurer

>
> Just my 2c.
>
> Regards,
> Jo
>

Aaron Meurer

unread,
Jan 26, 2012, 12:33:34 PM1/26/12
to sy...@googlegroups.com
On Wed, Jan 25, 2012 at 5:15 PM, Ronan Lamy <ronan...@gmail.com> wrote:
> Le mercredi 25 janvier 2012 à 13:36 -0700, Aaron Meurer a écrit :
>
>> - Should deprecation cycles be time based or version based?  On the
>
> Version-based deprecations are more convenient for users and developers,
> I think, but we'd need to set a release policy - define the difference
> between major and minor versions, and find a more constraining answer to
> "When do we release?" than "Whenever we feel like it."

Exactly. The whole original purpose of me writing this email was so
that we could start to agree on such a policy. But I see that it's
already started to diverge into other discussions.

>
>> - In https://github.com/sympy/sympy/pull/1009, we are changing the
>> syntax for Piecewise.
>
> In this case, it would be better to create a new object with a new name,
> considering that Piecewise isn't a good name (it's supposed to be
> piecewise-what, exactly?) and that the object is actually a
> case-expression. It would also allow to put its args in the right order,
> with the results after the corresponding tests.

Piecewise is the name used by Maple, Mathematica, and Sage (modulo a
capital letter). And to me, the args are in the right order now.
Maple does it the other way around, but this is just confusing to me.
The args are printed in the order that they are given in our syntax,
so to me this is most logical.

Aaron Meurer

Aaron Meurer

unread,
Jan 26, 2012, 12:38:49 PM1/26/12
to sy...@googlegroups.com
So are you -1 just to setting specific time/version lengths to
deprecate things, or is it more? Do you think it's a bad idea to even
say at all that we should decide beforehand how long something is
deprecated, and tell the user that? The reason I ask is that I want
to implement that API in SymPyDeprecationWarning so that you can call
it with args, like SymPyDeprecationWarning(feature="blah",
last_version="0.7.3", ...), and it would convert the string into
something like "Feature blah is deprecated and will be removed after
0.7.3...". But just having this in place will end up creating a soft
requirement that people use it.

Aaron Meurer

Aaron Meurer

unread,
Jan 26, 2012, 12:50:05 PM1/26/12
to sy...@googlegroups.com
On Thu, Jan 26, 2012 at 12:51 AM, Joachim Durchholz <j...@durchholz.org> wrote:
> Am 26.01.2012 01:58, schrieb Ronan Lamy:
>

I should perhaps point out that the 0 in front of our version number
says a little about the instability of our API. We do try to
deprecate removed features, where possible, but there is no guarantee
that any feature will not be changed, deprecated or not. This is
because we are still building things up. Modules with limited power
may have a limited API, which may simply not work when the power of
that module is extended. We can try to think of things beforehand,
but we can't rely on our ability to do this.

For example, when we update the polys to support symbolic algebraic
expressions (like QQ<sqrt(x)>), we will have to change the RootOf
syntax to name the variable to take the root of. We might not need to
deprecate in this particular case, as we could differentiate based on
how many args there are, but that's just the first example I could
think of.

Also, I should point out that it's fairly easy to run off of a local
copy of SymPy independent of whatever is installed system-wide, even
if you don't have admin access (you just have to add the directory to
the front of your PYTHONPATH).

A concern with time-based cycles is that they would affect how we
release. We might try to push out a release before the time is up, or
we might wait until the time is up before we release. Given how hard
it is to release, I wouldn't want any timelines to affect it, even
non-formally.

>
>
>>> Variant 2: Put the different Piecewise classes in different, mutually
>>> incompatible modules and instruct people to import one or the other, but
>>> not both.
>>> E.g. the module structure could look like this:
>>> sympy. - current API
>>> sympy.api1 - API 1 (deprecated)
>>> sympy.api2 - API 2 (deprecated, too)
>>> ...
>>> sympy.api10 - API 10 (last API we deprecated)
>>> sympy.api11 - API 11 (current)
>>>
>>> Modules in sympy will just load modules from api11.
>>>
>>> People who need long-term stability import their modules from
>>> sympy.apiNN.
>>> People who want to just hack something up import their modules from
>>> sympy.
>>
>>
>> Urgh!! That would mean we'd have to maintain 11 different APIs instead
>> of only one. That would be a nightmare.
>
>
> Old APIs don't need much maintenance. They don't even need to interoperate
> with the newer APIs (unless we want to make them do that).
>
> Think of them as old versions of SymPy, packaged as part of the whole
> system.

I do think of them as such, which is why I don't think they should be


packaged as part of the whole system.

>
> The alternative might be keeping old versions of SymPy around for download.

The are. You can also access any old version from git (run "git tag").

Aaron Meurer

krastano...@gmail.com

unread,
Jan 26, 2012, 1:43:54 PM1/26/12
to sy...@googlegroups.com
I mostly agree with Brian. Maybe adding a link to the issue tracker in
DeprecationWarning will be simpler than implementing
DeprecationWarning that warns about version numbers. And everything
will be in the issue tracker.

And the argument that Aaron gave against time based releases convinced me.

About the plotting module example:

next version: using Plot raises a warning that Plot will change and
it's better to use PygletPlot if you want the old behavior.
next +1 : The change is done. No warnings.

It was a bit more complicated in the initial idea, but by using
different namespaces there is now only one thing to be deprecated.

By the way, I would appreciate some help in reviewing it.

Joachim Durchholz

unread,
Jan 26, 2012, 1:47:52 PM1/26/12
to sy...@googlegroups.com
Am 26.01.2012 18:50, schrieb Aaron Meurer:
> I should perhaps point out that the 0 in front of our version number
> says a little about the instability of our API.

Hehe, I can understand :-)

Though I think it's not a bad idea to spend a few thoughts on how things
should roll after 1.0. We'll want to build the necessary infrastructure
before 1.0 after all.
Of course, it need not be right now :-)

> Also, I should point out that it's fairly easy to run off of a local
> copy of SymPy independent of whatever is installed system-wide, even
> if you don't have admin access (you just have to add the directory to
> the front of your PYTHONPATH).

It may be impossible to intall a matching Python interpreter due to
company security concerns (which would be bogus, but that doesn't help
the person who needs Sympy).

Or SymPy is used as part of an ongoing project. Project wants to move to
a newer Python, but does not want to upgrade SymPy at the same time
(e.g. it may have been programming around well-known bugs in SymPy for
years, and correcting all that would be a too large project for the time
budget available).

Just examples of things I have seen in my time.
As a general principle, if you have 20 tools and each introduces some
inflexibility that will bite you only 1% of the time where you do
something, you'll have a probability of 1-0.99**20 = 18% that any change
will fail. In other words, one in five change requests will not be
implementable - not good.

So any project that's supposed to be used for serious work should aim
for 100% backwards compatibility. I know that's not possible in
practice, and probably not worth implementing right here, right now, but
it's something to keep in mind put into the set of (conflicting) goals.

Regards,
Jo

Aaron Meurer

unread,
Jan 26, 2012, 3:38:08 PM1/26/12
to sy...@googlegroups.com
On Thu, Jan 26, 2012 at 11:47 AM, Joachim Durchholz <j...@durchholz.org> wrote:
> Am 26.01.2012 18:50, schrieb Aaron Meurer:
>
>> I should perhaps point out that the 0 in front of our version number
>> says a little about the instability of our API.
>
>
> Hehe, I can understand :-)
>
> Though I think it's not a bad idea to spend a few thoughts on how things
> should roll after 1.0. We'll want to build the necessary infrastructure
> before 1.0 after all.
> Of course, it need not be right now :-)
>
>
>> Also, I should point out that it's fairly easy to run off of a local
>> copy of SymPy independent of whatever is installed system-wide, even
>> if you don't have admin access (you just have to add the directory to
>> the front of your PYTHONPATH).
>
>
> It may be impossible to intall a matching Python interpreter due to company
> security concerns (which would be bogus, but that doesn't help the person
> who needs Sympy).

The system level Python should be fine. As long as it is 2.5-3.2 (as
of the next release), it should work. We recently dropped 2.4
support, so I suppose if you need to use a Python that ancient, you
are in trouble. I don't see us dropping 2.5 support any time soon, as
there would be little to gain from it.

>
> Or SymPy is used as part of an ongoing project. Project wants to move to a
> newer Python, but does not want to upgrade SymPy at the same time (e.g. it
> may have been programming around well-known bugs in SymPy for years, and
> correcting all that would be a too large project for the time budget
> available).

Again, these should be independent. If a new Python is released after
SymPy, it is unlikely that it will break an old SymPy. The worst I've
seen is a few minor bugs, and that only happens for major Python
releases. In other words, Python itself is very good at handling the
backwards compatibility issue you mention :)

We can also help this by building the release candidates for new
Python versions and fixing bugs before the new Python is released. But
problems really only occur in major Python releases, and these come
out so infrequently that this is almost a non-issue.

>
> Just examples of things I have seen in my time.
> As a general principle, if you have 20 tools and each introduces some
> inflexibility that will bite you only 1% of the time where you do something,
> you'll have a probability of 1-0.99**20 = 18% that any change will fail. In
> other words, one in five change requests will not be implementable - not
> good.
>
> So any project that's supposed to be used for serious work should aim for
> 100% backwards compatibility. I know that's not possible in practice, and
> probably not worth implementing right here, right now, but it's something to
> keep in mind put into the set of (conflicting) goals.
>
> Regards,
> Jo
>

In my experience with people on the mailing list, the things that
break for people from release to release are things that we didn't
even know about. Sometimes they are regressions, and sometimes they
are a result of something that seemed insignificant, or at least
unrelated, when we changed it. Few people ask about things that were
explicitly mentioned in the release notes. So I think we should try
to be very careful to note any kind of (public) API change in the
release notes.

But this is also just my 2¢. Maybe if we discuss it enough we can get
up to a dollar.

Aaron Meurer

Brian Granger

unread,
Jan 26, 2012, 5:30:21 PM1/26/12
to sy...@googlegroups.com
On Thu, Jan 26, 2012 at 9:38 AM, Aaron Meurer <asme...@gmail.com> wrote:
> So are you -1 just to setting specific time/version lengths to
> deprecate things, or is it more?

In some cases it is more. In some cases (like Real/Float) it is
possible to keep the older API around and put a dep. warning on it.
For the more mature parts of our code base, I am fine with this
approach - even adding version information like are you are proposing.
In other cases (like Piecewise) that is impossible without going
through horrible contortions. In those cases I don't think we should
make any attempt to deprecate the older API - just document it well in
release notes. Also, for less mature parts of the code base that are
still moving quickly, I don't think there is any need to issue
deprecation warnings. Basically, I feel that we should optimize for
ease of development, not API stability.

Aaron Meurer

unread,
Jan 27, 2012, 5:09:11 PM1/27/12
to sy...@googlegroups.com
I see. The main reason I wanted to deprecate the old API for
Piecewise rather than just removing it is that with the new syntax,
any Piecewise build from the old syntax will return something
unexpected, making it very easy to get silent wrong results or cryptic
errors. The double syntax is a little confusing, but it's able to
exist without any major conflicts (at least logically).

On Thu, Jan 26, 2012 at 3:30 PM, Brian Granger <elli...@gmail.com> wrote:
> On Thu, Jan 26, 2012 at 9:38 AM, Aaron Meurer <asme...@gmail.com> wrote:
>> So are you -1 just to setting specific time/version lengths to
>> deprecate things, or is it more?
>
> In some cases it is more.  In some cases (like Real/Float) it is
> possible to keep the older API around and put a dep. warning on it.
> For the more mature parts of our code base, I am fine with this
> approach - even adding version information like are you are proposing.
>  In other cases (like Piecewise) that is impossible without going
> through horrible contortions.  In those cases I don't think we should
> make any attempt to deprecate the older API - just document it well in
> release notes.  Also, for less mature parts of the code base that are
> still moving quickly, I don't think there is any need to issue
> deprecation warnings.  Basically, I feel that we should optimize for
> ease of development, not API stability.

I agree. Thinking about API leads to good development practices,
though, such as trying to make the API a good one from the start.

So while ease of development should indeed be the first priority,
users appreciate API stability, so when there's little difficulty on
our part to make it so, we should do that.

Stable APIs also help facilitate ease of development, as the different
parts of SymPy use each other, and every time an API changes, we have
to change all the code base as well as any open pull requests.

Aaron Meurer

Vladimir Perić

unread,
Aug 12, 2012, 11:55:50 AM8/12/12
to sy...@googlegroups.com
Hi,

First of all, sorry for resurrecting this - I was browsing the issue page, there's one that references this thread and I felt making a new thread would lose us some context. Anyway, in working on Twisted (my GSoC for this year) - which is a much more structured project, with many development processes which must be kept - I got some ideas on this.

My proposal is to, for each deprecation warning, show the version it has been deprecated since and a link to the relevant issue (as Stefan proposed). The only new "rule" we would need is that each deprecation should have an issue (but I think they all do already and it is good practice anyway). This way, we can decide on each issue individually (eg. .is_real() is harmless and can stick around; piecewise() should probably be changed asap etc. -- this is what I understood Brian and Stefan wanted), we are not tied to any particular release schedule* and we give the user real information and a handy link for more.

Code-wise, as far as I can see, SymPyDeprecationWarning has parameters to provide extra info (as requested in issue 2142 I guess[1]); these are currently "feature", "last_supported_version" and "useinstead". Per my proposal above, I would remove those three in favor of "since" and "ticket". I feel this would be more concise, and this information is easier to fit into a function decorator (in the sense that writing a long "useinstead" string is not very practical).

Thoughts?

[1] http://code.google.com/p/sympy/issues/detail?id=2142

* Which I think we should have anyway, but that's definitely a whole other thread; lets not discuss that here.

Chris Smith

unread,
Aug 12, 2012, 8:55:53 PM8/12/12
to sy...@googlegroups.com
No strong opinions, but I thought the useinstead was a good idea. And
if it gets longer one can always use _textwrap (or manual imports) to
create a private string that cna get passed as the useinstead value.

Aaron Meurer

unread,
Aug 13, 2012, 12:24:56 AM8/13/12
to sy...@googlegroups.com
On Sun, Aug 12, 2012 at 9:55 AM, Vladimir Perić <vlada...@gmail.com> wrote:
> Hi,
>
> First of all, sorry for resurrecting this - I was browsing the issue page,
> there's one that references this thread and I felt making a new thread would
> lose us some context. Anyway, in working on Twisted (my GSoC for this year)
> - which is a much more structured project, with many development processes
> which must be kept - I got some ideas on this.

I have no issue with resurrecting this, as I felt there wasn't really
enough discussion in the first place.

I'd love to hear from your experience with Twisted. Generally,
development processes are either a good thing or a bad thing, but you
usually can't really tell which unless you have direct experience with
it. Of course, some things that work well for one project won't work
well for another.

>
> My proposal is to, for each deprecation warning, show the version it has
> been deprecated since and a link to the relevant issue (as Stefan proposed).
> The only new "rule" we would need is that each deprecation should have an
> issue (but I think they all do already and it is good practice anyway). This
> way, we can decide on each issue individually (eg. .is_real() is harmless
> and can stick around; piecewise() should probably be changed asap etc. --
> this is what I understood Brian and Stefan wanted), we are not tied to any
> particular release schedule* and we give the user real information and a
> handy link for more.

I think this is a good idea. So we should extend
SymPyDeprecationWarning with two new fields, issue number, and
deprecated_since. And then fill it out for all the existing warnings
in the code.

>
> Code-wise, as far as I can see, SymPyDeprecationWarning has parameters to
> provide extra info (as requested in issue 2142 I guess[1]); these are
> currently "feature", "last_supported_version" and "useinstead". Per my
> proposal above, I would remove those three in favor of "since" and "ticket".
> I feel this would be more concise, and this information is easier to fit
> into a function decorator (in the sense that writing a long "useinstead"
> string is not very practical).

Right. Just as I said above. And we should definitely update
@deprecated too (but note that not all deprecations are functions, so
it won't always apply). I'm not concerned about long strings.
useinstead is usually just one word anyway.

Any other ideas here? I guess I'll start a branch to implement these
things, so we can get them for 0.7.2.

I guess one other thing we should do is add a label to the issue
tracker to mark issues for removing deprecated behavior.

>
> Thoughts?
>
> [1] http://code.google.com/p/sympy/issues/detail?id=2142
>
> * Which I think we should have anyway, but that's definitely a whole other
> thread; lets not discuss that here.

Well, feel free to open another thread about it. I really don't think
we can implement any kind of policy that will fix this. Regardless of
what we say we should or will do, unless there is someone or someones
who are willing to handle releases on a regular schedule, we will not
have them. I could say more, but again, let's do this on a new
thread.

Aaron Meurer

Vladimir Perić

unread,
Aug 13, 2012, 2:40:51 AM8/13/12
to sy...@googlegroups.com
I was going for brevity here, as all information kept inside a
"useinstead" can also be mentioned in the appropriate ticket, but you
are right - there's no real harm in keeping it there.

>
> --
> You received this message because you are subscribed to the Google Groups "sympy" group.
> To post to this group, send email to sy...@googlegroups.com.
> To unsubscribe from this group, send email to sympy+un...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/sympy?hl=en.
>



--
Vladimir Perić

Vladimir Perić

unread,
Aug 13, 2012, 2:48:39 AM8/13/12
to sy...@googlegroups.com
I actually thought they should replace the current three attributes,
but (and as Chris says above), we might as well keep them. I think
filling out these two should be "mandatory", while the rest should
only be used if they make sense.

>
>>
>> Code-wise, as far as I can see, SymPyDeprecationWarning has parameters to
>> provide extra info (as requested in issue 2142 I guess[1]); these are
>> currently "feature", "last_supported_version" and "useinstead". Per my
>> proposal above, I would remove those three in favor of "since" and "ticket".
>> I feel this would be more concise, and this information is easier to fit
>> into a function decorator (in the sense that writing a long "useinstead"
>> string is not very practical).
>
> Right. Just as I said above. And we should definitely update
> @deprecated too (but note that not all deprecations are functions, so
> it won't always apply). I'm not concerned about long strings.
> useinstead is usually just one word anyway.

Do we have some class decorators, too? I'm not sure anymore.. though I
guess not many classes get deprecated.

>
> Any other ideas here? I guess I'll start a branch to implement these
> things, so we can get them for 0.7.2.

Cool. I personally can't really work on it until GSoC ends (which is
in a week), but after that I'd love to help write/review it. Ping me
if you make a pull, in any case. Of course, no need to be hasty, maybe
someone else has a different idea or dislikes this one. :)

>
> I guess one other thing we should do is add a label to the issue
> tracker to mark issues for removing deprecated behavior.

Yes, probably a good idea, even if we don't end up implementing this.

>
>>
>> Thoughts?
>>
>> [1] http://code.google.com/p/sympy/issues/detail?id=2142
>>
>> * Which I think we should have anyway, but that's definitely a whole other
>> thread; lets not discuss that here.
>
> Well, feel free to open another thread about it. I really don't think
> we can implement any kind of policy that will fix this. Regardless of
> what we say we should or will do, unless there is someone or someones
> who are willing to handle releases on a regular schedule, we will not
> have them. I could say more, but again, let's do this on a new
> thread.
>
> Aaron Meurer
>

Aaron Meurer

unread,
Aug 13, 2012, 2:52:46 AM8/13/12
to sy...@googlegroups.com
On Mon, Aug 13, 2012 at 12:40 AM, Vladimir Perić <vlada...@gmail.com> wrote:
> On Mon, Aug 13, 2012 at 2:55 AM, Chris Smith <smi...@gmail.com> wrote:
>> No strong opinions, but I thought the useinstead was a good idea. And
>> if it gets longer one can always use _textwrap (or manual imports) to
>> create a private string that cna get passed as the useinstead value.
>
> I was going for brevity here, as all information kept inside a
> "useinstead" can also be mentioned in the appropriate ticket, but you
> are right - there's no real harm in keeping it there.

Well, useinstead is a pretty basic bit of information. If the user
can figure out how to replace deprecated function A with function B,
then he is good to go. He only needs to look for more information if
just replacing A with B doesn't work. That kind of information should
go on the issue.

Aaron Meurer

Aaron Meurer

unread,
Aug 19, 2012, 4:21:57 PM8/19/12
to sy...@googlegroups.com
I've started work on this at https://github.com/sympy/sympy/pull/1500.  I still need to modify the existing warnings to use the new flags.

Aaron Meurer

Aaron Meurer

unread,
Aug 25, 2012, 9:23:30 PM8/25/12
to sy...@googlegroups.com
OK, I've finished that work. I would appreciate a review, as this is
blocking on the release.

All deprecated features now have the issue and
deprecated_since_version flag. Additionally, all issues for
deprecated features are marked with the DeprecationRemoval tag
(http://code.google.com/p/sympy/issues/list?q=label:DeprecationRemoval),
so that people can easily find what features are marked for
deprecation.

Aaron Meurer
> --
> You received this message because you are subscribed to the Google Groups
> "sympy" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/sympy/-/zGsE1IPhS3IJ.
Reply all
Reply to author
Forward
0 new messages