Our Typer source install took 14 seconds and its build took 1 second, but the test command failed with 8 failures. Typer is fastapi/typer, the Python library that turns function parameters and type hints into terminal arguments, options, and generated help. Choose it for a new CLI whose syntax you control. For an established tool, begin with the commands your users already depend on: direct Click integrations and argparse-style option lists can make keeping the existing parser the better decision.
Two documented changes shape that decision. Typer 0.26.0 vendored Click, and Typer 0.29.0 introduced Pydantic parameter validation. The source run used commit 7350016; the supplied latest-release record identifies 0.29.1. Repository and README, Click migration guide, 0.29.0 release notes.
What people use typer for
GitHub contributors describe using Typer for shared application code, asynchronous work behind synchronous commands, and application management commands.
| Who and source | What they used it for | Reported outcome |
|---|---|---|
| rec, issue #106 | Shared code between a Typer CLI and ordinary Python callers in a proprietary production project | Reported reduced repetition using a helper later released as dtyper |
| jessekrubin, issue #88 | Started asynchronous work after synchronous argument parsing | Reported success for basic tasks and supplied an asyncio.run wrapper |
| killswitch-GUI and team, issue #88 | Used management commands to call application servers | Encountered trouble when adding existing asynchronous calls |
| dd-ssc, issue #140 | Replaced conflicting quiet and verbose flags | Reported adopting one enum-based log-level option |
The rec account connects Typer to an existing application rather than a standalone greeting script. The helper is part of that result: the contributor used additional code to share behavior between CLI and non-CLI callers. That makes thin command wrappers a useful design to consider when the same business function also serves tests or another application entry point. Contributor account.
Bala Priya C's KDnuggets tutorial provides a different example: a JSON-backed schedule tracker with task, time, and priority commands. It is a teaching exercise. Its command layout and expanded code contain defects discussed below. Schedule tracker tutorial.
What happened when we ran it
The source installation and build succeeded at commit 7350016, followed by a failed test command with eight assertion failures.
The November 12, 2026 source checks recorded the following results:
| Source-check step | Our recorded result | What it means for adoption |
|---|---|---|
| Install | Succeeded in 14 seconds; 43 dependencies recorded, 44 MB on disk | The recorded source environment installed successfully |
| Build | Succeeded in 1 second | The configured build step completed |
| Tests | Failed, exit 1 after 167 seconds; 1,629 passed, 8 failed, 29 skipped | The suite failed in this sandbox |
| pip-audit | 17 known vulnerabilities | Review the dependency advisories before distributing this environment |
| Repository scan | 783 files, about 36,900 source lines, 2.1 MB checked out | The installed environment occupied more space than the checkout |
| Development signals | 16 CI workflow files, a tests directory, no Dockerfile detected | The repository includes test automation files |
The sandbox configuration specified 3 CPUs, 8 GB RAM, and image label ghcr.io/astral-sh/uv:python3.12-bookworm. The test tail reports 8 failed, 1,629 passed, 29 skipped, and 3 xfailed in 165.52 seconds. The parser summary separately labels its result "of 1637".
All eight failed assertions concern CLI output. Two help tests expected a default containing "Deadpoolio the amazing's name". Two enum-help tests expected the placeholder <list[Eggs|Bacon|Cheese]>. One printing test expected a formatted data representation; another expected emphasis around "Alert! Portal gun shooting!".
The tuple-arity test expected "2 values are required, but 1 given." The file-error test expected "unknown error", while the captured output included a boxed "Could not open file" message. Maintainers whose tests compare help or error messages literally should include those outputs in an adoption check.
The separate hands-on attempt failed its safety prerequisite because none of compose.yaml, compose.yml, docker-compose.yaml, or docker-compose.yml existed. Its harness required a Compose file. A generic run record nevertheless says the repository was deployed with its own Docker Compose, with every port bound to 127.0.0.1. The failed prerequisite contradicts that deployment description. Teardown reported "ok". Failed prerequisite, conflicting run description, teardown.
Compose was a requirement of that harness. The README's installation instructions use a Python project environment.
Where people get stuck: shared code and established syntax
Historical issue threads show friction when developers preserve an existing Python interface or shell command while adding Typer.
| Report | Workaround or resolution in the record | Decision consequence |
|---|---|---|
| #88: unawaited coroutine | jessekrubin supplied a synchronous command that calls asyncio.run | Account for the wrapper around asynchronous application code |
| #106: OptionInfo defaults | Maintainer advised supplying every parameter; users separated wrappers or added helpers | Keep ordinary application defaults separate from CLI metadata defaults |
| #153: shared options missing from subcommand help | Participants discussed reusable Option declarations and parent callbacks with context | Decide where users should discover common flags |
| #110: variable-length option values | Repeated flags and fixed-length tuples were offered | Compare exact invocations before replacing argparse |
| #533: union annotations | Maintainer explicitly reported a fix in 0.12.4 | Treat this report as a historical fixed bug |
In #88, killswitch-GUI described management commands that started application servers and then needed existing async calls. jessekrubin's response kept parsing synchronous and started the asynchronous function afterward. Another participant identified the maintenance cost: separate functions with repeated arguments. The wrapper solves a concrete integration problem while adding code that the application must maintain. Async discussion.
Issue #106 concerns calling command functions directly from Python. The maintainer advised passing every parameter. Later commenters described omitted arguments receiving OptionInfo objects when Option declarations occupied the Python defaults. Separating ordinary business functions from CLI wrappers makes that distinction explicit. Cyclopts' comparison also describes Annotated metadata as a way to remove proxy defaults. Direct-call discussion, Cyclopts comparison.
Read the resolution as well as the issue status. In #76, the maintainer suggested an enum for Literal-style choices, followed by requests to reopen the discussion. By contrast, #533 contains an explicit statement that its fix is available in 0.12.4. Those are different kinds of evidence.
Upgrading across 0.26.0 and 0.29.0 changes the checklist
The Click and Pydantic changes affect different parts of an existing CLI: integrations first, then parameter validation and output.
| Version boundary | Documented change | What to inspect in your application |
|---|---|---|
| 0.26.0 | Click source moved inside Typer; direct Click integration became unsupported | Extracted Click apps, Click plug-ins, and Click-specific type overrides |
| 0.29.0 | Pydantic became required for parameter validation | Accepted input, defaults, datetime parsing, and error output |
The vendoring guide says direct Click use is no longer supported. Applications depending on it must move to plain Typer or Click itself. That is a concrete reason to retain Click even when Typer's function declarations appeal to you. Resolve the integration requirement before translating command signatures. Official migration guidance.
The 0.29.0 release notes describe a gradual migration focused on preserving compatibility, while naming changes to validation errors, help text, and usage output. They remove click_type from Option and Argument, tighten some defaults, change datetime parsing, and remove several Click attributes from callback parameter objects. The notes specifically replace a tuple default containing three None values with an optional tuple whose default is None. Release-specific changes.
Use an existing command as the migration specimen. Record its normal invocation and representative invalid input, then compare these contracts on the intended release:
| Existing contract | What to compare |
|---|---|
| One option followed by several values | The shell invocation and any need for repeated flags |
| Options shared across commands | Parent help, subcommand help, and flag placement |
| A command also called from Python | Omitted defaults and explicitly supplied arguments |
| Asynchronous application functions | Wrapper signatures and the event-loop entry point |
| Custom types and callbacks | Removed click_type arguments and Click parameter attributes |
| Errors consumed by automation | Exit behavior and captured error text |
Older Click pinning advice belongs to an earlier dependency arrangement. In #377, users reported import failures around Click 8.1.0 and restored operation with 8.0.4. Typer's later vendoring changes that relationship. Apply the migration guidance for the release you intend to use rather than carrying the historical pin forward automatically.
Where a larger command tree hurts: imports and completion
Issue #231 reports slower completion as imports accumulate, making completion latency a useful part of a large-CLI evaluation.
The original reporter described response time above 100ms after adding a few imports. Later discussion considered deferring application imports. The concern is the delay while entering a command, before its useful work starts. Completion discussion.
Evaluate help and completion after adding the dependencies that real commands load. A greeting script exercises a different import path from a management command that loads an application package. Where the design permits, defer expensive application imports until the command needs them and compare the same workload again.
Shared options introduce another maintenance choice. In #153, a parent callback centralizes values, while participants report that common flags appear in parent help rather than each subcommand's help. Reusing option declarations keeps the parameters in individual signatures. Choose the help layout deliberately so users can find a flag where they expect it.
Security defaults: keep local variables out of error logs
Typer's exception documentation says local-variable display has been disabled by default since 0.23.0 to avoid exposing passwords, keys, and tokens.
The documentation calls out CI logs as a place where those values could be retained. Setting pretty_exceptions_show_locals=True adds local values to debugging output. Leave that setting disabled for commands handling credentials, and review the application's own logging before sharing failed-job output. Exception documentation.
Our pip-audit result reported 17 known vulnerabilities. That count concerns the recorded dependency environment. Review its advisories before distributing a CLI built from that environment. Separately, the 0.29.0 release notes list escaping terminal controls in Pydantic validation errors as an upstream fix. Commands that start servers or change application data also need permissions appropriate to those operations.
Maintenance evidence supports activity, not an adoption count
The November 12, 2026 repository snapshot records 20,345 stars, a November 11 last push, and release 0.29.1 published on November 11.
The metadata also records 48 open issues and pull requests combined. Recent release activity gives maintainers a reason to read current documentation alongside historical issue threads. Stars measure repository attention; the combined issue field includes both issues and pull requests. Repository metadata, release record.
The documentation rating is 4/5 under this editorial rubric: 1 means essential guidance is missing, 2 means major workflows lack explanation, 3 means the core workflow is covered, 4 adds concrete migration and operational guidance, and 5 requires consistent guidance across the reviewed pages. The README covers simple and grouped commands, the migration guide explains the Click boundary, and the exception guide explains sensitive-value handling. The stale Pydantic statement on the alternatives page keeps this assessment below 5. Setup ease, community, and maturity render as N/A for the reasons collected in the final section.
Typer vs Click, argparse and Cyclopts
Choose among these tools by declaration style, required integrations, and the command syntax your users need to keep.
| Candidate | Reason to consider it | When it changes the decision |
|---|---|---|
| Typer | Uses typed Python function parameters to declare a CLI | Consider it for a new interface whose command syntax you control |
| Click | Its documentation demonstrates explicit parameter decorators | Prefer it when direct Click functionality or plug-ins are required |
| argparse | The standard library parser includes nargs and mutually exclusive groups | Keep it when existing parsing behavior is part of the command contract |
| Cyclopts | A participant in #336 identifies it as an alternative that parses docstrings | Compare it when parameter documentation in docstrings is a requirement |
The Click recommendation follows Typer's own migration guide. Click's documentation supplies a decorator-based example and describes nested commands and lazy loading of subcommands. The argparse API documents argument specifications and mutually exclusive groups. Those are concrete interface differences to compare with an existing application. Typer migration guide, Click documentation, argparse reference.
The Cyclopts recommendation is attributed to sherbang in #336. Cyclopts' own comparison dates its original analysis to Typer 0.9.0 and notes later updates. Read that document with its stated version context. Contributor recommendation, dated comparison.
What the other reviews get wrong
The supplied pages contain a stale Pydantic statement, outdated separate typer-cli installation advice, and defective schedule-tracker code.
Typer's alternatives page says Pydantic is not used by Typer. The 0.29.0 release notes explicitly say parameter validation now uses Pydantic. Use the release notes for that dependency decision. Stale comparison, 0.29.0 release.
The GeeksforGeeks completion instructions recommend installing typer-cli separately. The maintainer's archived Typer-CLI answer says the command became part of Typer in 0.12.1, and the supplied README says installation includes it in the project environment. If the shell cannot find the command, check the active environment and executable path first. Article, maintainer answer.
The KDnuggets tutorial initially registers one command but shows an invocation containing add-task. Typer's documented default omits the command name when the app has only one command. Its expanded example also places a command decorator before a print statement, references an undefined i, and spells another decorator @appcommand(). These are visible defects in that tutorial's supplied code. Tutorial, documented command layout.
Questions people ask
Existing integrations and parsing requirements usually settle the choice more clearly than a hello-world comparison.
Should I choose Typer or Click for a Python CLI?
Consider Typer for declarations based on typed function parameters. Choose Click when direct Click functionality is an application requirement; Typer's migration guide explicitly identifies that migration path.
How do Typer, Click, and argparse compare?
Typer uses function parameters, Click demonstrates parameter decorators, and argparse uses a parser specification in the standard library. Compare the declarations alongside the exact inputs, help, and errors your application needs.
When should I keep argparse instead of migrating to Typer?
Keep it when preserving its existing option grammar matters more than changing declaration style. Issue #110 records contributors holding off on migration over variable-length option values.
How can I run asynchronous functions from Typer commands?
jessekrubin's workaround in #88 uses a synchronous command that starts asynchronous work with asyncio.run after parsing. Include the wrapper and its repeated parameters in your maintenance estimate.
Can I define shared options once and show them in each subcommand's help?
Issue #153 discusses reusable declarations in command signatures and a parent callback with context. Participants report that the parent approach places common options in parent help. Choose the approach around the help layout you need.
Can I call Typer command functions directly from Python and preserve their defaults?
Inspect the defaults themselves. Issue #106 records OptionInfo objects reaching direct callers when CLI metadata occupies the defaults. Separate ordinary business functions from CLI wrappers or supply explicit arguments.
How do I make Typer options mutually exclusive?
The #140 discussion proposes enum choices, subcommands, and validation callbacks. One contributor replaced quiet and verbose flags with an enum-based log-level option, expressing the choice through one parameter.
Can Typer accept a variable-length list after one option flag?
The historical #110 discussion offers repeated flags or fixed-length tuples. Those forms change the invocation requested by contributors migrating from argparse. Include the original invocation in your release-specific evaluation.
What we have not tested yet
The supplied execution evidence covers commit 7350016 in one sandbox, with no completed end-user CLI exercise from the separate Compose attempt.
The image label records configuration, not actual interpreter or operating-system versions. Raw toolchain logs are needed to identify those versions and reconcile the parser's test denominator with the separately reported outcomes. The assertion excerpts show output mismatches but do not identify their causes or connect them to a dependency or the Pydantic migration. The audit count lacks advisory identities, severities, affected versions, and runtime reachability; it supports neither a diagnosis of 17 exploitable Typer flaws nor a remediation list.
The historical threads do not establish current native async support, Literal support, automatic parameter-docstring parsing, shared-help behavior, or variable-length option compatibility in 0.29.1. Their workarounds remain attributed historical examples. The completion discussion supplies no current reproducible benchmark, and deferring imports carries no promised speedup. The pack includes no side-by-side workload comparison with Click, argparse, or Cyclopts, no adoption census, and no support-response study. rec's proprietary-project result remains that contributor's account.
Setup ease has no numerical rating because the source install lacks an exercised end-user CLI workflow and actual toolchain identification. Community support and runtime maturity also have no numerical ratings: issue participation and one source-suite run do not measure support capacity or production reliability. The documentation rating assesses the supplied pages, rather than compatibility across releases.
The Compose attempt stopped before a functional task, despite the contradictory generic deployment description. The pack contains no completed known-answer exercise or screenshot records. The tutorial defects were identified from supplied text; no corrected tutorial was executed. This revision adds no execution result. Rendered layout, related links, recommendation modules, accessibility, and template behavior also remain outside this text review.
Before replacing a working parser, select one awkward command that mixes shared options, a direct Python caller, or asynchronous work. Compare its help, defaults, invalid-input errors, and exact shell invocation on the intended release. That gives the migration decision a concrete application requirement.

