Giter VIP home page Giter VIP logo

docsig's Introduction

Hi there 👋

  • 🔭 I’m currently working with Python
  • 🌱 I’m currently learning Rust
  • 👯 I’m looking to collaborate on any Python projects that need help
  • 🤔 I’m looking for help with any issues you find here
  • 📫 How to reach me: [email protected]

docsig's People

Contributors

dependabot[bot] avatar eford36 avatar fresh2dev avatar jshwi avatar pre-commit-ci[bot] avatar snyk-bot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

docsig's Issues

`@typing.overload`-ed methods not supported

Following on from #196 - sorry, I probably should have highlighted methods as important to me.

When docsig checks the following code:

class SomeClass:

    @overload
    def process(self, response: None) -> None:
        ...
    @overload
    def process(self, response: int) -> tuple[int, str]:
        ...
    @overload
    def process(self, response: bytes) -> str:
        ...
    def process(self, response):
        """process a response.

        :param response: the response to process
        :return: something depending on what the response is
        """
        ...  # actual implementation goes here

I get the following error:

no_vc/test_docsig_methods.py:12 in SomeClass
--------------------------------------------
def process(✖None) -> ✓str:
    """
    :param response: ✖
    :return: ✓
    """

E102: includes parameters that do not exist

I had a go debugging and think I've tracked down the issue:

inserting:

                        if func.name == "process":
                            breakpoint()

just before this line

and then running subnode.args.arguments shows that the self argument isn't prevent, as it appears to be for a non-overload-ed method:

debugging session:

> docsig/_module.py(61)__init__()
-> func = _Function(
(Pdb) subnode.args.arguments
[<AssignName.response l.12 at 0x1058eeb80>]
(Pdb) 

Then, when creating the _Signature, docsig pops the first argument if the relevant function is a (non-static) method, which in this case is the 'actual' first argument response.

Feature request - pre-commit integration & links

Feature request

I realy like this library and find that it has some features that are missing in the pydocstyle. 🎉

Feature 1 🚀

Since many users automate linters, it would be nice to have support for the pre-commit. :)

Feature 2 🚀

When path to a file is displayed, it would be nice if it was a link to the specific line (like in pydocstyle). I'm using PyCharm if that's of any relevance.

Import error on `sphinxcontrib.napoleon` on Python 3.10.8

Hey! I'm getting this ImportError when running docsig --help:

ImportError: cannot import name 'Callable' from 'collections'

docsig requires sphinxcontrb-napoleon directly, to its latest version of 0.7 which was released in 2018.

As seen in this commit to sphinxcontrib-napoleon, the issue I'm facing was fixed in 2021. I tried copy-pasting that commit into my venv and docsig --help ran correctly.

I see 2 alternatives:

  1. Given that napoleon now comes bundled with sphinx (at sphinx.ext.napoleon), docsig should have a direct requirement on sphinx. sphinx is quite big, though, and you may not want this.
  2. Specify a git dependency [ref] for sphinxcontrib-napoleon, knowing that that repo is not kept up to date.

I can work on a PR to fix this with whatever solution you prefer.

False positive when return annotation is a string

Hello,

Here's a minimal example:

def example(some_input: int) -> int:
    """
    Do something.

    Args:
        some_input: Random integer

    Returns:
        Unchanged input
    """
    return some_input

This works like a charm but, when the return type is 'int' following error is returned. Sometimes, the return type has to be represented as a string due to dependency issues.

def example(✓some_input) -> ✖None:
    """
    :param some_input: ✓
    :return: ✖
    """

E104: return statement documented for None

Support for Python 3.7.x

Hey @jshwi! I came across this tool because of a comment of yours on StackOverflow. I wanted to use your tool for a small project that I'm currently working on, but poetry cannot resolve the dependencies because my tool is built for Python 3.7.2+, and yours for 3.8+.

Is there a reason for not to support Python 3.7? If so, is there anything I can do to help add support for it?

Report when the parameter is spelled wrongly

somemethod(myparam) -> str:
    """ What the method does
    :param nottherightparam: what the param does
    :return: str
    """

is not reported in version 0.13.0. However, if another mistake is added, e.g. I remove the return type hint and cause a E109, then the output shows that the parameter is wrong.

And a minor thing: docsig -v doesn't return anything, but docsig --version works

Parameter and return value is not correctly recognized

Hey I just came across the project and would love to use it for larger projects to avoid faulty documentation after e.g. refactoring of code.
Now I have a function:

def modify(numericString: Union[str, int]) -> str:
    '''Do stuff.

    Parameters
    ----------
    numericString: Union[str, int]
        numeric string that should be converted.

    Returns
    -------
    str
        reformatted string
    '''
    numericString = str(numericString)
    last = numericString[-1]
    middle = numericString[-3:-1]
    first = numericString[:-3]
    finalstring = "-".join([first, middle, last])
    return finalstring 

Now I get the error:

def modify(✖numericString) -> ✓str:
    """...

    :param None: ✖
    :return: ✓
    """

E103: parameters missing

Another example I encountered:

def check_stuff(str_lin: str) -> bool:
    """Check if "A" or "B".

    The function checks wether the string is "A" or "B".

    Parameters
    ----------
    str_lin: str
        special string produced by function_of_y

    Returns
    -------
    bool
        Returns True, else false
    """
    if any(s in str_lin for s in ["A", "B"]):
        return True
    return False

The error:

def check_stuff(✖str_lin) -> ✖bool:
    """...

    :param None: ✖
    :return: ✖
    """

E103: parameters missing
E105: return missing from docstring

I am a bit confused as I cannot really pinpoint the issue. Is this a parsing or formatting problem of the docstring? Any obvious error here?
Please ignore the random weird functions, they are just examples similar to conde I have written.

args and kwargs are not recognized as generic arguments

Hi,

I just came across an error (or expected behavior?):

I have the Fuction with the docstring

def mocked_requests_get(*args: str, **kwargs: str) -> Response:
    """Mock the expected Response from the API based on the received url via args.

    Returns
    -------
    Response
        response object
    """

which leads to the Error:

def mocked_requests_get(✖*args, ✖**kwargs) -> ✓Response:
    """
    :param None: ✖
    :param None: ✖
    :return: ✓
    """
E103: parameters missing

Not sure if this should be the case usually I would not annotate args/kwargs as they are no specific function arguments. Or is my notion wrong here?

We are using numpy type docstrings in case this is relevant. Happy to provide more info if I forgot anything relevant.

Enhancement suggestion: Add capability to check __init__ document in its own docstring

Firstly, thanks for docsig, it's great!

One area where I still am finding 'mistakes' in my docstring signatures though is where I've added the description of an __init__ signature in the __init__ docstring itself, rather than the class docstring. If I miss a parameter, or document a parameter that doesn't exist there, there's no way to configure docsig to raise an error, without it also being unhappy about the signature being documented in the __init__ docstring rather than the class docstring.

Minimal example

I think this is understood that this isn't covered (it's on the README page), so putting this inside a 'details' as I think my job here is more persuading that it's worth docsig supporting this, rather than describing what it currently does - but I want to have that too of course.

For a minimal example, consider this code:

class SomeClass:
    \"\"\"This class does something useful.\"\"\"

    def __init__(self, first_arg: bool, second_arg: str):
        \"\"\"Create a new instance of SomeClass
        
        :param first_arg: this does exist
        :param non_existent_arg: this arg doesn't actually exist!
        \"\"\"
        pass

This has incorrect documentation (so rendered docs will show incorrectly), but will pass a docsig check with default configuration):

from docsig import docsig
    
string_to_check = """
class SomeClass:
    \"\"\"This class does something useful.\"\"\"

    def __init__(self, first_arg: bool, second_arg: str):
        \"\"\"Create a new instance of SomeClass
        
        :param first_arg: this does exist
        :param non_existent_arg: this arg doesn't actually exist!
        \"\"\"
        pass
"""

docsig(string=string_to_check, check_class=True)

Note though that if I turn on check_class, even a correctly documented __init__ will cause an error E103: parameters missing:

from docsig import docsig
    
string_to_check = """
class SomeClass:
    \"\"\"This class does something useful.\"\"\"

    def __init__(self, first_arg: bool, second_arg: str):
        \"\"\"Create a new instance of SomeClass
        
        :param first_arg: this does exist
        :param second_arg: this exists too and completes coverage
        \"\"\"
        pass
"""

docsig(string=string_to_check, check_class=True)

this gives:

5 in SomeClass
--------------
class SomeClass:
    """
    :param None: ✖
    :param None: ✖
    """

    def __init__(✖first_arg, ✖second_arg)?:

E103: parameters missing

However, I think this is a valid way of writing docstrings, and one where docsig would add value if it checked this style of writing them (I found this by realising I had made a mistake of this kind in my project).

In fact, my reading of PEP257's multi-line docstring section is that this is the recommended way of writing __init__ signatures:

The class constructor should be documented in the docstring for its __init__ method

From this paragraph on docstrings for classes:

The docstring for a class should summarize its behavior and list the public methods and instance variables. If the class is intended to be subclassed, and has an additional interface for subclasses, this interface should be listed separately (in the docstring). The class constructor should be documented in the docstring for its __init__ method. Individual methods should be documented by their own docstring.

I think probably for backwards-compatibility, we would probably want to leave the check of an __init__ docstring only running when check_class is True.

Would you be interested in docsig supporting this?

If so, I may be able to work on a PR myself, if we can agree on an approach.

Some initial thoughts on supporting this

My impression is that it's [these lines](https://github.com/jshwi/docsig/blob/7fc0f02c71f8cb469c41763dee2ccf0bba2f6e38/docsig/_function.py#L292-L295) (copied below for inline clarity that set the docstring of `__init__` method's to the parent class's docstring, which prevents this being possible:

        self._docstring = _Docstring(
            node.doc_node if not self.isinit else self._parent.doc_node,
            ignore_kwargs,
        )

There's a couple of approaches that I wonder if they're worth considering:

  1. allow the constructor(/__init__) of _function._Docstring to take a str for node, as well as _ast.Const or None, and then replace:
    node.doc_node if not self.isinit else self._parent.doc_node,
    with:
    node.doc_node if not self.isinit else self._parent.doc_node.value + "\n" + node.doc_node.value,
    This will fix the problems I currently have, but does allow weird things where some params are documented in the class docstring, and others in the __init__ docstring, which is undesirable, but seems unlikely to happen.
  2. Add a class method on _function._Docstring to create a _Docstring from a string, which the above code will call in the self.isinit case (this preserves the constructor signature of _Docstring but is otherwise similar to the above
  3. Move the if not self.isinit logic inside _Docstring.__init__, create _Matches(...) for both the __init__ docstring and the parent class docstring, and use whichever one gets matches for the _Docstring._string, _Docstring._args and _Docstring._returns. If neither, choose the class docstring, if both match maybe raise an error? Might have to be a new one. The downside of this is that it's a bigger change, and there's more compute involved in deciding which docstring to use for __init__ methods, which is then 'throwaway work' if check_class isn't even true.

Skip checking for one line docstings

Hello,

It would be good if checking could be skipped somehow for one line docstrings.

For example

def example(some_input: int) -> int:
    """Return input."""
    return some_input

Sometimes the effort to write the full doctoring is just too high. This is also fine with the linter pydocstyle.

Would you consider adding an option to ignore docstrings that are in one line?

Issue with pre-commit integration

Hello @jshwi,

Trying to test this I found a bug related to the pre-commit configuration.

pre-commit-config.yaml

-   repo: https://github.com/jshwi/docsig
    rev: v0.33.0
    hooks:
      - id: docsig
        name: docsig
        entry: docsig
        language: system
        types: [python]
        require_serial: true
        args:
          - "-i"
          - "--ignore-kwargs"

I get an error: docsig: error: unrecognized arguments: --ignore-kwargs

Then I tried manually and the version wasn't the latest. After the manual update (pip install -U docsig), both manual and pre-commit worked.

This shouldn't be the case since I've specified the exact version in the pre-commit (I guess it should have it's own environment based on the yaml specification).

Can you check if this is due to bad integration?

Regarding the feature itself, everything seems to be working as expected so far. 🎉

Best, Lazar

docsig doesn't seem to support `@typing.overload`-ed functions/methods well

Hi,

Consider this code, modified from the typing docs to include a docstring:

from typing import overload

@overload
def process(response: None) -> None:
    ...
@overload
def process(response: int) -> tuple[int, str]:
    ...
@overload
def process(response: bytes) -> str:
    ...
def process(response):
    """process a response.

    :param response: the response to process
    :return: something depending on what the response is
    """
    ...  # actual implementation goes here
docsig checking code

from docsig import docsig
    
string_to_check = """
from typing import overload

@overload
def process(response: None) -> None:
    ...
@overload
def process(response: int) -> tuple[int, str]:
    ...
@overload
def process(response: bytes) -> str:
    ...
def process(response):
    \"\"\"process a response.

    :param response: the response to process
    :return: something depending on what the response is
    \"\"\"
    ...  # actual implementation goes here
"""
docsig(string=string_to_check)

This currently throws a bunch of docsig errors:

4
-
def process(✖response) -> ✓None:
    ...

E113: function is missing a docstring

7
-
def process(✖response) -> ✖tuple[None]:
    ...

E113: function is missing a docstring

10
--
def process(✖response) -> ✖str:
    ...

E113: function is missing a docstring

13
--
def process(✓response)?:
    """
    :param response: ✓
    :return: ✖
    """

E109: cannot determine whether a return statement should exist or not

But to my mind this is documented in a way that seems reasonable.

The fact that Sphinx supports this for rendering docstrings with autodoc/autosummary makes me think this is do-able (their code for supporting this is here), but it seems like this may significantly increase the complexity of docsig's docstring parsing code.

A couple of ways of opting out of supporting this for docsig:

  1. Document it as unsupported, don't change the default behaviour but allow comments like '# docsig off' that turn off docsig checks for a function
  2. Turn off all checks for overload-ed functions/methods, and document this. However, this still requires figuring out which functions/methods are overload-ed, which is still potentially a reasonable amount of work/change to the existing logic.

Positional-only arguments not recognized

Thank you for this fantastic tool!

I am using it to validate and correct existing docstrings. I found an issue I can't seem to overcome, though, related to the use of positional-only arguments.

For example, given this function and docstring...

def starmap(
    self,
    fun: Callable[..., Any],
    iterable: Sequence[Sequence[Any]],
    /,
    *args: Any,
    timeout: float = 0,
    show_progress: bool | None = None,
    **kwargs: Any,
) -> list[Job]:
    """Submits many jobs to the queue -- one for each sequence in the iterable. Waits for all to finish, then returns the results.

    Args:
        fun: ...
        iterable: ...
        *args: static arguments passed to the function.
        timeout: ...
        show_progress: ...
        **kwargs: static keyword-arguments passed to the function.

    Returns:
        ...
    """

...I cannot overcome this error reported by docsig:

---------------------------------
def starmap(✖*args, ✖timeout, ✖show_progress, ✖**kwargs, ✖None, ✖None) -> ✓list[Job]:
    """
    :param fun: ✖
    :param iterable: ✖
    :param args: ✖
    :param timeout: ✖
    :param show_progress: ✖
    :param kwargs: ✖
    :return: ✓
    """

E102: includes parameters that do not exist
E101: parameters out of order

Handling of *args and **kwargs

Hello,

I'm not sure what's the convention of documenting *args and **kwargs but I think they are usually skipped.

What do you think about ignoring them (if they are not present in the docstring)?

Sphinx 7.0 compatibility

Hi,

At the moment, docsig declares a dependency of:

Sphinx = ">=4.3.2,<7.0.0"

which means you can't install docsig in the same virtual environment as a project that wants to build it's own docs with Sphinx 7.0 or greater.

However, from a quick look, it doesn't seem like it needs to rule out 7.0.0 for the correct operation of docsig - the only place I can see sphinx used from a quick search is as import sphinx.ext.napoleon as _s in docsig/_function.py, and even there, it's the Napoleon extension specifically, and it doesn't look to me that the way it's used would be broken by 7.0.

I'm guessing you want to restrict to <7.0.0 for building the docs of docsig, but could that be done as a dev dependency, and the 'main' dependency just be Sphinx = >=4.3.2` or similar?

This isn't affecting me directly (it was, but I just changed my pre-commit config so docsig got installed in its own virtual environment rather than as a 'local' hook, which is best practice anyway).

Let me know what you think, and if you think there is work (beyond the normal testing of PRs etc.) to support sphinx 7, I can potentially pick it up

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.