I think you can safely claim that you aren't a beginner anymore.
This style:
from typing import Callable
from typing import Generic
from typing import NewType
from typing import Optional
from typing import Sequence
from typing import TYPE_CHECKING
from typing import TypeVar
is kind of nasty. I've seen it recommended before by some linters but not others. isort by default does the opposite, and tuple-imports as in
from typing import Callable, Generic, ...
This is a matter of taste.
I hope that when you say viv.main you actually mean viv.__main__, which is a built-in package structure better suited to main entry.
Pythonistas variously claim that logic-by-exception is either endemic to the language or standard practice. I find it gross and avoidable. So things like this:
try:
# this will capture escaped 'foo' with or without <requirement>
_, package_ref, requirement = package.split("'", 2)
except ValueError: # not enough values to unpack - i.e. escaped string not found
pass
else:
return package_ref, requirement or None
are better expressed as
# this will capture escaped 'foo' with or without <requirement>
parts = package.split("'", 2)
if len(parts) == 3:
_, package_ref, requirement = parts
return package_ref, requirement or None
# ...
Or if you want to be even fancier, read about PEP636 structural pattern matching. Similar story for your KeyError.
I find this:
package_ref can be 1 of the following
1. Local directory (which must contain a viv.toml, or viv will report an error).
2. Project or archive URL.
3. Local Repo.
4. A known alias to any of the above
to make life harder for everyone - the programmer and the user. This kind of clever attempt at inference kneecaps validation and requires heuristics. You're better off having four mutually exclusive command-line arguments. Then, if the user passes something that doesn't smell like a URL, you can error out before sending the request to the HTTP stack (among other things).
Separate away your argparse definitions from your main method.
As in another review, don't assert in production code. Raise exceptions instead (and not AssertionError!)
Don't raise SystemExit where you've done it - instead, just exit().