Docstrings and type hints - #12
Conversation
|
Thank you for this PR. As you may have already noticed, there are some merge conflicts.
Your PR in the original repository will automatically get updated with your latest push. |
|
Yes, I have seen merge conflicts. I was waiting for your comment and approval on solving them. Thank you for hints. I will solve merge conflicts possibly fast as I can. |
|
I have resolved merge conflicts. Now everything should be fine. I will briefly explain the idea that guided me in sloving the problem. |
There was a problem hiding this comment.
Hi Rhobar9,
Firstly, thank you for your work in improving the documentation. It's great to see your dedication, and your efforts have been genuinely valuable.
As we continue to work on this, I wanted to share some insights into how we can make our docstrings even more effective.
Ideally, a docstring should explain the purpose or intention of a function rather than the technical details of how it
accomplishes its task. This will make it easier for other developers to understand why a particular function exists and
how it fits into the overall project.
A good example of this practice is the docstring you have added to the 'optimize' method:
def optimize(self) -> (NDArray, ArrayLike):
"""Eliminate redundant simplices and then sets appropriate mask.
Returns:
alpha_opt: the most accurate alpha value based on minimal amount of simplices
shape: shape after optimization
"""This is a great docstring, as it explains the purpose of the function and what it returns.
In contrast, the docstring you've written for the function is more about what it does internally:
def __init__(self, coords: NDArray) -> None:
"""In try block function invokes __init__ method of class
Triangulation from matplotlib package and sends
to it default set of coordinates. If ValueError occurs,
function will tackle it.
"""While this is technically correct, it doesn't tell us much about the purpose of the function.
Also, I think it is preferable for __init__ methods to be left withtout docstrings. Instead we can add relative
information to the docstring of the class (see the comment I have added to the relevant lines).
Sometimes, we have classes or functions whose purpose is quite evident from their names. In such cases, the aim of the docstring should be to provide any additional context or explanation that might not be immediately obvious from the name. It should be more generic, outlining its potential usage, rather than detailing a very specific use case.
For example, consider the NotEnoughPoints exception:
class NotEnoughPoints(AlphaException):
"""If instance of class Delaunay has less than 3 points, this exception will be raised."""
passIn this case, the name of the exception already gives a good indication of when it might be used, and the docstring seems to limit its usage to just the Delaunay class. It might be more beneficial to provide a general explanation, like so:
class NotEnoughPoints(AlphaException):
"""Raised when an operation requires a certain number of points and that condition is not met."""
passIn this way, we maintain the flexibility of our exception, and the docstring serves as a general guideline rather than a strict rule.
Please take a look at the docstrings you have added and see if you can apply these principles to them.
You may find that some of them are already quite good, and don't need to be changed. In other cases, you may find that a
docstring only reiterates what is already clearly stated in the function name. In such cases, it is better to leave the docstring out entirely.
Feel free to reach out if you have any questions or need further clarification.
| """In try block function invokes __init__ method of class | ||
| Triangulation from matplotlib package and sends | ||
| to it default set of coordinates. If ValueError occurs, | ||
| function will tackle it. | ||
|
|
There was a problem hiding this comment.
Ideally, a docstring should explain the purpose or intention of a function rather than the technical details of how it accomplishes its task. Also I think it is best not to add docstrings directly to the init method of any class. Instead, consider adding the relevant info to the class doctstring. One example would be:
"""
Parameters:
-----------
coords: NDArray,
The coordinates of the points (... possibly add more explanations here ...)
Raises:
-------
ValueError: When so and so occurs
"""
| Delaunay is abstract class, which derives from matplotlib.tri.Triangulation. | ||
| It adds set of coordinates and essential methods. |
There was a problem hiding this comment.
First row repeats what is already stated clearly in the code.
2nd row is too vague.
I would prefer if you somehow attempted to retain the original info, i.e. that the intention of this class is to implement a similar interface as scipy.spatial.Delaunay
(I actually cannot recall why this decision was made in the first place. This makes keeping this info in the docstrings especially important)
|
|
||
|
|
||
| class NotEnoughPoints(AlphaException): | ||
| """If instance of class Delaunay has less than 3 points, this exception will be raised.""" |
There was a problem hiding this comment.
In this case, the name of the exception already gives a good indication of when it might be used, and the docstring seems to limit its usage to just the Delaunay class. It might be more beneficial to provide a general explanation, like soIn this case, the name of the exception already gives a good indication of when it might be used, and the docstring seems to limit its usage to just the Delaunay class. It might be more beneficial to provide a general explanation, like so
"""Raised when an operation requires a certain number of points and that condition is not met."""|
I also noticed that the .idea directory from your local IntelliJ IDEA configuration has been inadvertently committed to the repository. This directory typically contains user-specific settings and should generally not be part of the version-controlled code. To rectify this, I've updated the .gitignore file in the repository to exclude the .idea directory. Here are the steps you should follow to remove the .idea files from your branch:
These steps will ensure that the As always, if you have any questions or need further assistance, please let me know. |
|
Hi @panosz! |
"Rectify_problem_with_redundant_idea_files"
|
Hey @Rhobar9 |
|
Hi @panosz ! |
|
No pressure! |
|
Hi again @panosz ! |
panosz
left a comment
There was a problem hiding this comment.
Hi @Rhobar9,
Thank you for your contribution to the project. Your effort in improving the documentation is evident, and it's a valuable addition to the codebase.
I've added a few review comments throughout the code, mainly focusing on improving clarity and consistency in the docstrings. My primary goal is to maintain a coherent style throughout the project and ensure that our documentation is as clear as possible for both developers and users.
Please understand that these suggestions come from a place of wanting to enhance the overall quality of our project. I genuinely appreciate the time and energy you've invested in this contribution.
If you have any questions or concerns about the feedback, or if you'd like to discuss any of the points further, please don't hesitate to reach out.
| Args: | ||
| x: Contains all x values of triangle's points. | ||
| y: Contains all y values of triangle's points. | ||
|
|
||
| Returns: | ||
| outcome: Contains squared circumradius of internal triangle. |
There was a problem hiding this comment.
Please, retain info of the expected shape of x and y.
It is also OK to document parameters together, if their particular meaning is clear by their name. In this case, it should be perfectly clear that x refers to the x coordinate and y to the y coordinate.
| @property | ||
| def simplices(self): | ||
| def simplices(self) -> NDArray: | ||
| """Create simplices property essential to further operations.""" |
There was a problem hiding this comment.
Properties don't "create" — they return or set values. Consider rephrasing for clarity, eg: "Return the collection of triangles" or something similar
|
|
||
| def __len__(self): | ||
| def __len__(self) -> int: | ||
| """Return amount of object's edges.""" |
There was a problem hiding this comment.
clear, but to maintain consistency, it might be worth specifying that it returns the number of simplices
| """Abstract class, which provides useful interface. This idea is similar to scipy.spatial.Delaunay solution. | ||
| Coordinates of future Alpha_Shaper object will be added via Delaunay class. | ||
| If the coordinates do not meet the conditions, the class will raise an appropriate error. | ||
| It also adds key methods. | ||
|
|
There was a problem hiding this comment.
Consider rephrasing, for conciseness.
Consider documenting the coords parameter of __init__.
Also, try to standardize the info on when initialization raises an exception with the following pattern
"""
Raises:
- NotEnoughPoints: If there are fewer than 3 points provided.
- ValueError: For other value-related issues with the coordinates.
"""
I would be extra grateful, if you tried to incorporate here any info of when the base class raises an exception
| """Crucial class to creating alpha-shapes. | ||
| The class handles points, creates internal triangles and generates shapes. |
There was a problem hiding this comment.
Please, try to document the parameters of init.
Info about normalize can be found in README and example scripts.
| """Eliminate redundant simplices and then set appropriate mask. | ||
|
|
||
| Returns: | ||
| alpha_opt: the most appropriate alpha value based on minimal amount of simplices. | ||
| shape: shape after optimization. |
There was a problem hiding this comment.
This is a bit inaccurate. From where are the redundant simplices eliminated?
As it is, the docstring suggests that the method has side effects, i.e. somehow modifies the state of the object. Is this correct? What does this method return?
| """Calculate the squared circumradius of triangle. | ||
| See more about it on: `https://en.wikipedia.org/wiki/Circumscribed_circle`. |
| Args: | ||
| lengths: contains lengths of triangle's sides. | ||
|
|
||
| Returns: | ||
| Contains values of squared circumradiuses. |
There was a problem hiding this comment.
Consider documenting the type and shape of the arguments and output
| """Create internal triangle from given simplices. | ||
|
|
There was a problem hiding this comment.
It is not clear what an "internal triangle" is. Consider rephrasing, for clarity.
|
|
||
|
|
||
| def plot_alpha_shape(ax, alpha_shape): | ||
| """Mainline function, which plots all sets of figure's points. |
There was a problem hiding this comment.
In general, I find that it is best to avoid characterizations such as "mainline", "central" and so on in docstrings, unless there is really good reason to do it.
|
I have just pushed the code. I will describe my doubts and the scope of work in detail in the issue discussion. |
panosz
left a comment
There was a problem hiding this comment.
Thank you for your contribution.
Please take a look at the comments in the review.
|
|
||
|
|
||
| class AlphaException(Exception): | ||
| """Abstract class for exceptions which could be raised during the work of Alpha_Shaper class.""" |
There was a problem hiding this comment.
This class definition is self explanatory. No need to add a docstring.
- Remember docstrings come at a cost. They add extra visual info. In this case, I think it should be avoided, because it offers no extra info other than what the reader might have guessed at a glance, if the docstring was not there.
|
|
||
| class OptimizationWarnging(UserWarning): | ||
| class OptimizationWarning(UserWarning): | ||
| """Warns user without interrupting the program.""" |
There was a problem hiding this comment.
This is more like a personal note of how warnings work. Also it is not very accurate, since you can always change the way warnings are treated (e.g. ignore them or treat them as errors). I would remove this docstring.
|
|
||
| class Delaunay(Triangulation): | ||
| """ | ||
| """Abstract class with useful interface. |
There was a problem hiding this comment.
Avoid using the term "abstract class", unless you are using abstract base classes and feel that you need to communicate this to the user
| """Abstract class with useful interface. | ||
| Visitor sublclass of matplotlib.tri.Triangulation. | ||
| Mimics scipy.spatial.Delaunay interface. | ||
| See similar idea on scipy.spatial.Delaunay solution. |
There was a problem hiding this comment.
Actually what I meant is that this class implements (part of) the interface of scipy.spatial.Delaunay. I do not remember why I made this choice. Consider replacing this line with the previous sentence (or an improved version of it)
| Args: | ||
| coords(NDArray): raw coords at which preparation process will be performed. | ||
|
|
||
| Raises: | ||
| - NotEnoughPoints: If there are fewer than 3 points provided. | ||
| - ValueError: For other value-related issues with the coordinates. | ||
| """ |
There was a problem hiding this comment.
Nice. Try hinting the user about the expected shape of the coords parameter
| """Return the alpha value that allows plotting the shape with the minimum number of triangles. | ||
| Vertices of initial triangulation aren't left uncovered. | ||
|
|
||
| Returns: | ||
| alpha_opt(NDArray): optimized alpha value. | ||
| shape(ArrayLike): shape based on the optimized alpha value. | ||
|
|
There was a problem hiding this comment.
No need to mention plotting. Although plotting the alpha shape is an obvious option, we don't want to assume too much about the users' intentions. Focus on what this method does, not on how we think its output should be used.
| ----------- | ||
| Args: | ||
| points: array-like, shape(N,2) | ||
| coordinates of the points | ||
|
|
||
| Returns: | ||
| -------- | ||
| points: array, shape(N,2) | ||
| normalized coordinates of the points | ||
| points: array, shape(N,2) | ||
| normalized coordinates of the points | ||
|
|
||
| center: array, shape(2,) | ||
| coordinates of the center of the points | ||
|
|
||
| center: array, shape(2,) | ||
| coordinates of the center of the points | ||
| scale: array, shape(2,) | ||
| scale factors for the normalization |
There was a problem hiding this comment.
Thank you for suggesting an alternative docstring standard. I have been using the numpy standard for years, but your way also looks nice
| where | ||
| r_c = \frac {abc}{4{\sqrt {s(s-a)(s-b)(s-c)}}} | ||
| See: `https://en.wikipedia.org/wiki/Circumscribed_circle` | ||
| def _circumradius_sq(lengths: NDArray) -> Union[float, np.inf]: |
There was a problem hiding this comment.
This type hint raises an exception for python3.8:
File "examples/alpha_shape_exmpl.py", line 4, in <module>
from alpha_shapes import Alpha_Shaper, plot_alpha_shape
File "/home/panosz/Documents/programming/python/alpha_shapes/alpha_shapes/__init__.py", line 1, in <module>
from .alpha_shapes import Alpha_Shaper
File "/home/panosz/Documents/programming/python/alpha_shapes/alpha_shapes/alpha_shapes.py", line 259, in <module>
def _circumradius_sq(lengths: NDArray) -> Union[float, np.inf]:
File "/usr/lib/python3.8/typing.py", line 261, in inner
return func(*args, **kwds)
File "/usr/lib/python3.8/typing.py", line 358, in __getitem__
parameters = tuple(_type_check(p, msg) for p in parameters)
File "/usr/lib/python3.8/typing.py", line 358, in <genexpr>
parameters = tuple(_type_check(p, msg) for p in parameters)
File "/usr/lib/python3.8/typing.py", line 149, in _type_check
raise TypeError(f"{msg} Got {arg!r:.100}.")
TypeError: Union[arg, ...]: each arg must be a type. Got inf.
In later versions it is not a problem, but are you sure we can have a Union with np.inf, which is not a type?
| def _calculate_cirumradius_sq_of_triangle(x: ArrayLike, y: ArrayLike): | ||
| """ | ||
| calculates the squared circumradius of a triangle with coordinates x, y | ||
| def _calculate_cirumradius_sq_of_triangle(x: ArrayLike, y: ArrayLike) -> NDArray: |
| @@ -1,9 +1,21 @@ | |||
| """This module contains mechanisms essential to printing figures. | |||
There was a problem hiding this comment.
This script has been changed in main and there are merge conflicts.
There was a problem hiding this comment.
for the moment focus on the other one and we can resolve them later
Commits add docstrings and type hints for alpha_shapes.py and plotting.py files. Docstrings follow Google Style Python Docstrings pattern. Please, keep particular attention at type hints. I have checked it many times. However it was most challenging part of task, in few methods flow of object is quite advanced. Maybe I made a mistake somwhere.
I am open for hints and discussion. If need of amendments occurs, I will rewrite code.
Regards, Rhobar9