Skip to content
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

Newly created project crashes when calling with --version parameter #752

Closed
e2jk opened this issue Aug 2, 2021 · 6 comments · Fixed by #769
Closed

Newly created project crashes when calling with --version parameter #752

e2jk opened this issue Aug 2, 2021 · 6 comments · Fixed by #769
Assignees
Labels
bug Something isn't working

Comments

@e2jk
Copy link
Contributor

e2jk commented Aug 2, 2021

Associated Template/Command/Core

poetry run python <project_name>\__main__.py --version

After having created a fresh new project, running the unchanged code with the --version flag ends up in a Traceback:
RuntimeError: Could not determine the version for None automatically.

The main() function has the @click.version_option() decorator defined, without options. Should Click's version_option be passed the version number explicitly like this?
@click.version_option(version="0.1.0")

Making this change results in the expected behavior:

>poetry run python timelog_tracker\__main__.py --version
timelog_tracker, version 0.1.0

Or will that cause issues with cookietemple's version bumping?

That function's documentation has some info about importlib.metadata.version() being used to determine the version if it is not passed, maybe this means this code works fine when running from a full package, but shouldn't it also work when running from a development setup?

Full Traceback (Click to expand)
C:\Users\<username>\Documents\devel\timelog_tracker>poetry run python timelog_tracker\__main__.py --version
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ C:\Users\<username>\Documents\devel\timelog_tracker\timelog_tracker\__main__.py:15 in <module>       │
│                                                                                                  │
│   12                                                                                             │
│   13 if __name__ == "__main__":                                                                  │
│   14 │   traceback.install()                                                                     │
│ ❱ 15 │   main(prog_name="timelog_tracker")  # pragma: no cover                                   │
│   16                                                                                             │
│                                                                                                  │
│ C:\Users\<username>\AppData\Local\pypoetry\Cache\virtualenvs\timelog-tracker-cUVxxgV3-py3.8\lib\site │
│ -packages\click\core.py:1137 in __call__                                                         │
│                                                                                                  │
│   1134 │                                                                                         │
│   1135 │   def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any:                           │
│   1136 │   │   """Alias for :meth:`main`."""                                                     │
│ ❱ 1137 │   │   return self.main(*args, **kwargs)                                                 │
│   1138                                                                                           │
│   1139                                                                                           │
│   1140 class Command(BaseCommand):                                                               │
│                                                                                                  │
│ C:\Users\<username>\AppData\Local\pypoetry\Cache\virtualenvs\timelog-tracker-cUVxxgV3-py3.8\lib\site │
│ -packages\click\core.py:1061 in main                                                             │
│                                                                                                  │
│   1058 │   │                                                                                     │
│   1059 │   │   try:                                                                              │
│   1060 │   │   │   try:                                                                          │
│ ❱ 1061 │   │   │   │   with self.make_context(prog_name, args, **extra) as ctx:                  │
│   1062 │   │   │   │   │   rv = self.invoke(ctx)                                                 │
│   1063 │   │   │   │   │   if not standalone_mode:                                               │
│   1064 │   │   │   │   │   │   return rv                                                         │
│                                                                                                  │
│ C:\Users\<username>\AppData\Local\pypoetry\Cache\virtualenvs\timelog-tracker-cUVxxgV3-py3.8\lib\site │
│ -packages\click\core.py:923 in make_context                                                      │
│                                                                                                  │
│    920 │   │   )                                                                                 │
│    921 │   │                                                                                     │
│    922 │   │   with ctx.scope(cleanup=False):                                                    │
│ ❱  923 │   │   │   self.parse_args(ctx, args)                                                    │
│    924 │   │   return ctx                                                                        │
│    925 │                                                                                         │
│    926 │   def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]:                 │
│                                                                                                  │
│ C:\Users\<username>\AppData\Local\pypoetry\Cache\virtualenvs\timelog-tracker-cUVxxgV3-py3.8\lib\site │
│ -packages\click\core.py:1379 in parse_args                                                       │
│                                                                                                  │
│   1376 │   │   opts, args, param_order = parser.parse_args(args=args)                            │
│   1377 │   │                                                                                     │
│   1378 │   │   for param in iter_params_for_processing(param_order, self.get_params(ctx)):       │
│ ❱ 1379 │   │   │   value, args = param.handle_parse_result(ctx, opts, args)                      │
│   1380 │   │                                                                                     │
│   1381 │   │   if args and not ctx.allow_extra_args and not ctx.resilient_parsing:               │
│   1382 │   │   │   ctx.fail(                                                                     │
│                                                                                                  │
│ C:\Users\<username>\AppData\Local\pypoetry\Cache\virtualenvs\timelog-tracker-cUVxxgV3-py3.8\lib\site │
│ -packages\click\core.py:2364 in handle_parse_result                                              │
│                                                                                                  │
│   2361 │   │   │   ctx.set_parameter_source(self.name, source)  # type: ignore                   │
│   2362 │   │   │                                                                                 │
│   2363 │   │   │   try:                                                                          │
│ ❱ 2364 │   │   │   │   value = self.process_value(ctx, value)                                    │
│   2365 │   │   │   except Exception:                                                             │
│   2366 │   │   │   │   if not ctx.resilient_parsing:                                             │
│   2367 │   │   │   │   │   raise                                                                 │
│                                                                                                  │
│ C:\Users\<username>\AppData\Local\pypoetry\Cache\virtualenvs\timelog-tracker-cUVxxgV3-py3.8\lib\site │
│ -packages\click\core.py:2326 in process_value                                                    │
│                                                                                                  │
│   2323 │   │   │   raise MissingParameter(ctx=ctx, param=self)                                   │
│   2324 │   │                                                                                     │
│   2325 │   │   if self.callback is not None:                                                     │
│ ❱ 2326 │   │   │   value = self.callback(ctx, self, value)                                       │
│   2327 │   │                                                                                     │
│   2328 │   │   return value                                                                      │
│   2329                                                                                           │
│                                                                                                  │
│ C:\Users\<username>\AppData\Local\pypoetry\Cache\virtualenvs\timelog-tracker-cUVxxgV3-py3.8\lib\site │
│ -packages\click\decorators.py:387 in callback                                                    │
│                                                                                                  │
│   384 │   │   │   │   )                                                                          │
│   385 │   │                                                                                      │
│   386 │   │   if version is None:                                                                │
│ ❱ 387 │   │   │   raise RuntimeError(                                                            │
│   388 │   │   │   │   f"Could not determine the version for {package_name!r} automatically."     │
│   389 │   │   │   )                                                                              │
│   390                                                                                            │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
RuntimeError: Could not determine the version for None automatically.

To Reproduce

Steps to reproduce the behavior:

  1. Create a new project using cookietemple, don't modify anything else
  2. Run the project's main__.py file passing it the --version flag, in my case:
    poetry run python timelog_tracker\__main__.py --version
  3. See Traceback indicating the version number couldn't be identified

Expected behavior

I would have expected the program to output the version number that was indicated when creating the project.

System:

  • OS: Windows 10 Enterprise build 1803, French language
  • Python: 3.8.2 (handled by pipx)
  • Virtual environment: pipx
  • cookietemple: 1.3.9

Additional context

None

@e2jk e2jk added the bug Something isn't working label Aug 2, 2021
@Zethson Zethson added Windows Windows only and removed Windows Windows only labels Aug 3, 2021
@Zethson
Copy link
Member

Zethson commented Aug 3, 2021

Interesting finding. I can reproduce this.

It works with the cookietemple repository: poetry run python cookietemple/__main__.py --version but not for a freshly created project.
I will look into this and fix it.

@Zethson
Copy link
Member

Zethson commented Aug 3, 2021

We can fix it by adapting the __main__.py to:

#!/usr/bin/env python
"""Command-line interface."""
import click
import {{ cookiecutter.project_slug_no_hyphen }}
from rich import traceback


@click.command()
@click.version_option(
    {{ cookiecutter.project_slug_no_hyphen }}.__version__,
    message=click.style(f"{{ cookiecutter.project_name }} Version: { {{ cookiecutter.project_slug_no_hyphen }}.__version__ }", fg="blue"),
)

However, this would require the package to be installed and does not work with poetry run python out of the box.

  1. Are users even using poetry run python? I guess most just use poetry run after poetry install?
  2. We can also patch this by hardcoding the version and adding it to the cookietemple.cfg file (which is used for bump-version)

@e2jk
Copy link
Contributor Author

e2jk commented Aug 3, 2021

How about this, which works in a development setting (I suppose __init__.py does get packaged and would work after installing from PyPI - I haven't tested this though):

from __init__ import __version__

@click.command()
@click.version_option(version=__version__)

That way, no need to redefine the version, we just use the value defined in __init__.py which does get updated after a version bump.

As to using poetry run python, I got in the habit of doing so after having issues on Windows with simple poetry run, for example that the cmd virtual environment console doesn't allow me to reuse a previous command [up or down arrows], or just now it fails to run:

C:\Users\<username>\Documents\devel\timelog_tracker>poetry run

  FileNotFoundError

  [WinError 2] Le fichier spécifié est introuvable

  at c:\users\ke4843\appdata\local\programs\python\python38-32\lib\subprocess.py:1307 in _execute_child
      1303│             sys.audit("subprocess.Popen", executable, args, cwd, env)
      1304│
      1305│             # Start the process
      1306│             try:
    → 1307│                 hp, ht, pid, tid = _winapi.CreateProcess(executable, args,
      1308│                                          # no special security
      1309│                                          None, None,
      1310│                                          int(not close_fds),
      1311│                                          creationflags,

@Zethson
Copy link
Member

Zethson commented Aug 3, 2021

from __init__ import __version__

@click.command()
@click.version_option(version=__version__)

I guess this would have to be

from {{ cookiecutter.project_slug }} import __version__

@click.command()
@click.version_option(version=__version__)

Could do that. I'll try it out soonish.

@e2jk
Copy link
Contributor Author

e2jk commented Aug 3, 2021

Hi @Zethson, I've been looking a bit more around, and I don't know if the approach of importing from __init__.py isn't going to cause more trouble down the road...

Looking at this SO discussion, there doesn't seem to be one best solution.
Seems like a lot of people create a separate version.py file and import that both from their __init__.py, setup.py and other sourcefiles, the main reason being to prevent circular imports/dependencies.
If you're creating a Python package, in __init__.py you would import from you main files so as to make your module's function accessible to other code that would import your package. But if those files import from __init__.py to get the version number, you get into a circular dependency import, basically breaking your module.

Maybe the most pragmatic would be your suggestion at the end of this post:

We can also patch this by hardcoding the version and adding it to the cookietemple.cfg file (which is used for bump-version)

Since you already have all the infrastructure to update hardcoded values, one more place where it would get updated shouldn't be a problem. And that way we prevent causing issues for people that would want to use cookietemple to create Python modules... What do you think?

@Zethson
Copy link
Member

Zethson commented Aug 4, 2021

Since you already have all the infrastructure to update hardcoded values, one more place where it would get updated shouldn't be a problem. And that way we prevent causing issues for people that would want to use cookietemple to create Python modules... What do you think?

Yeah, might then be the most reasonable solution. Any opinion @Imipenem ?

Imipenem added a commit that referenced this issue Aug 12, 2021
- version is now hardcoded in templates and will be bumped as well
@Imipenem Imipenem linked a pull request Aug 12, 2021 that will close this issue
Zethson added a commit that referenced this issue Aug 12, 2021
* Fix #752: Project version using poetry run

- version is now hardcoded in templates and will be bumped as well

* fix f-string

Co-authored-by: Lukas Heumos <[email protected]>
@Zethson Zethson closed this as completed Aug 12, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants