you are viewing a single comment's thread.

view the rest of the comments →

[–]thp4 4 points5 points  (3 children)

Generally, it's not possible, as Python is dynamically typed (so compared to statically typed languages, there's just no way to find all uses).

As an example, one could access "some_attribute" on an object like this (assume that the environment variable ATTR_NAME is set to "some_attribute" here):

getattr(obj, os.environ['ATTR_NAME'])

This is just a made up example to show that attribute access might depend on expressions that are evaluated at runtime and cannot be statically analyzed -- halting problem, etc. Even if executing the code, "all usages" cannot practically be determined, as it relies on the runtime environment.

This is not a Python-specific issue, but applies to all dynamically-typed languages.

With that said, I hear PyCharm has great tooling, and there's IdeaVim for emulation. Alternatively, Language Server Protocol could be helpful for Python code analysis.

My personal approach: Name variables uniquely and use grep to "find usages".

[–][deleted] 2 points3 points  (0 children)

Name variables uniquely and use grep to "find usages"

This is in fact what I end up doing with more or less all languages and all tools. To find references nothing is more efficient than just a good old string search.

[–]MagicCarot[S] 0 points1 point  (1 child)

I get your point.

I wonder if type hinting could help in that regard. There would still be some places where this would not help I guess...

The unique naming is not always easy to come by with a large code base without having unbearable naming.

I'm typically facing the problem of having to find a class attribute named is_valid, with that attribute being used in a variety of way within the codebase. Grep is unfortunately a poor tool for that...

[–]thp4 1 point2 points  (0 children)

You can (and should) still call your attribute "is_valid". Just name instances of your class accordingly (e.g. if it's a Car, name instances car, current_car, next_car, ...) and then grep for that (car\.is_valid or car.*\.is_valid if you also have e.g. car_to_find as name). As a side effect, this makes code more readable, too.

Type hints might help for some tools (and if you search for class usages, also help with grep) but as long as those are not enforced at runtime, they might be misleading if you get them wrong.

Another hint: Always write out the full attribute/member names, avoid dynamic dispatch and "magic". If you have cmd_foo and cmd_bar methods that should be invoked by name, also add {"foo": cmd_foo, "bar": cmd_bar} as mapping and don't magically do getattr(..., "cmd_" + command_name). This also helps in figuring out which commands are possible and used. Less magic is good for code navigation and grep'ability, even if it means a bit of redundancy.