-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Finalize sorting of class bodies #11
Comments
One issue, raised by hacker new user drcongo, is the use of |
Hi @bwhmather - here's the order in our internal guidelines I mentioned on HN, given that this is quite Django specific, it doesn't go as far as your proposed list (ie: there's a lot of dunder methods you never see in Django models), and it also accounts for some Django specifics like @cached_property decorators...
Like I say, this is very Django specific, and also possibly quite specific to the way we do things, so could be to niche for edit: Oh, and |
@drcongo - I think ssort is always going to group private methods above any public methods for consistency with function sorting and the "always scroll up for dependencies, down for uses" rule. Do you group lifecycle ( |
I've been thinking about this a bit more since last week, and I think there are two questions I have which it might be possible to answer by scanning a large representative corpus of python code:
|
Order proposed by @AnonymouX47 in #26:
|
One thing that annoyed me today. I have a class that controls the lifecycle of a resource. It has a Unfortunately, some of the error handling paths in the event handling methods need to be able to shutdown the resource. This means that all the shutdown code needs to go at the top. I don't like this. |
The fact is... no matter how much deliberation happens, the group order chosen will be opinionated in one way or the other. So, I propose that a reasonable default order be chosen but beyond that, a user should be able to customize the grouping and group ordering as desired and even (maybe later) be able to include user-defined groups and change the group a statement belongs to. So, For the custom group ordering, there could be:
For the addition of custom groups and to change the group a statement belongs:
A statement can belong to only one group. The particularly-styled comment can be of the form:
for custom group ordering and
for custom grouping. |
My suggestion above goes beyond the scope of the topic, I simply put it all down as it came to mind... but please help reference it in a more appropriate issue, if any. Thanks. |
I quite like @AnonymouX47's order, it's not dissimilar to ours and makes sense to my brain, but yeah, it's likely to be a very personal preference for most users. |
I think we're all broadly in agreement. I'm inclined to follow the spirit of @AnonymouX47's opening paragraph but am leaning towards enabling customization by trying to make the default only enforce things which are relatively uncontroversial. I like the idea of customized group ordering. It works well in isort, where you can also add rules for creating custom groups, but it's a little beyond what I feel confident dedicating the time to implement right now. It's also something we can do post 1.0 without breaking backwards compatibility. I think I will open a new issue with a "help wanted" tag, and possibly revisit it later. Regarding a default grouping, there certainly seems to be no disagreement that we should have one, or on the positioning of docstrings, My current feeling is that there are really only two unforgivable problems with the current order:
The first is bad enough that we can probably just treat it as a bug, and fix it. For the second, I think there are basically two options for how we can dial it back:
|
I personally do that just to keep them together with the lifecycle methods, since they're also d'under methods.
I think that's okay. When custom group ordering is implemented, maybe an optional "private" group could be added.
I feel that's okay for the default... personally, I don't. I think inner classes should definitely be on top as @drcongo suggested. As for topological sorting based on self references, I think applying it to private methods only will be a better option. Just wanna put this out there... I feel *Personally, I'm not a fan of conventions lacking a valid reason. |
I thought about this as well, since ssort resorted my class in an inconsistent way I did not like. Consider the following class: class Config:
def _read(self):
return {}
def __init__(self):
self.reset()
self._config = self._read()
def _write(self):
def reset(self):
self._write()
def reload(self):
self._config = self._read()
def __getitem__(self, name):
return self._config[name]
def __setitem__(self, name, value):
self._config[name] = value
self._write()
def __str__(self):
return "" The above sorting is the one proposed by ssort. My main issue is that
SolutionAssuming we have a class (or a module) without recursion, I propose the following approach: Step 1: build the dependency graphFor my example it looks as follows: class Config
├── def __init__: ...
│ ├── def _read: ...
│ └── def reset: ...
│ └── def _write: ...
├── def reload: ...
│ └── def _write: ...
├── def __getitem__: ...
├── def __setitem__: ...
│ └── def _write: ...
└── def __str__: ... The dependency graph forms a acyclic tree. We can use this property to sort it. Step 2: Sort all childs of the rootThe root of the graph is the
One choice could be lexiographic but def __init__: ...
├── def _read: ...
└── def reset: ...
└── def _write: ...
def __getitem__: ...
def __setitem__: ...
└── def _write: ...
def reload: ...
└── def _write: ...
def __str__: ... The relative position of these methods is now fixed. Step 3: Repeat step 2 with new roots.Next we remove the root from the dependency graph, consider each of its childs as new roots and repeat step 2 for each tree in the forest. Recall that all trees in the forest are already sorted! Again, we add each roots child before the root and sort lexigraphically (inside of each tree): def _read: ...
def reset: ...
└── def _write: ...
def __init__: ...
def __getitem__: ...
def _write: ...
def __setitem__: ...
def _write: ...
def reload: ...
def __str__: ... Step 4+: We repeat this procedure recursively until there are no trees left:def _read: ...
def _write: ...
def reset: ...
def __init__: ...
def __getitem__: ...
def _write: ...
def __setitem__: ...
def _write: ...
def reload: ...
def __str__: ... Finally: remove duplicatesAs a last step, we remove all methods the appear multiple times, starting from the bottom and we get the following order: def _read: ...
def _write: ...
def reset: ...
def __init__: ...
def __getitem__: ...
def __setitem__: ...
def reload: ...
def __str__: ... This way of sorting would be, IMO, the most logical way of sorting functions or methods in a class. TL, DR (now for modules, not classes)
|
Before sorting topologically,
ssort
currently pre-sorts class members in the following order:__slots__
and__doc__
).__init__
or__del__
) in fixed order.I think this is basically sensible but, unlike topological sorting of top-level statements, there is no strong argument for it to be this way.
I'd like to invite comments or opinions on how this order could be improved before the 1.0 release.
The text was updated successfully, but these errors were encountered: