Skip to main content

Count lines of code in Python projects with comment-aware breakdowns

Project description

count-lines

Count lines of code across multiple languages with a comment-aware breakdown.

Supports Python, JavaScript (incl. JSX), TypeScript (incl. TSX), and Rust. Scans local folders or shallow-clones public git repositories on the fly.

Supported languages

Language Extensions
Python .py
JavaScript .js, .mjs, .cjs
JSX .jsx
TypeScript .ts
TSX .tsx
Rust .rs

JSX files are parsed with the JavaScript grammar (the modern JS grammar handles JSX natively).

Installation

uv tool install python-count-lines

Run pcl -v to check the installed version.

Usage

pcl                                 # current directory
pcl path/to/repo                    # a specific folder
pcl src/app.py                      # a single file
pcl github.com/psf/requests         # a public repo (shorthand)
pcl https://github.com/psf/requests # full URL also works

Output

  count-lines
  Path:    /home/me/projects/mixed-app

  Folders          12
  Source files    47
    typescript    28
    rust          12
    python         7
  Total lines  9,820
    code       6,540
    doc lines  1,180
    comments     520
    blank      1,580

  Top 5 largest files
  backend/src/lib.rs              1,420
  web/src/components/App.tsx        984
  backend/src/handlers.rs           910
  web/src/utils/parser.ts           802
  scripts/migrate.py                610

When --exclude is used, the headline rows (folders, files, total) carry a dim delta showing how much was filtered out:

  Folders         1 | -50% of 2
  Source files    7 | -36% of 11
  Total lines   427 | -26% of 580

Remote repositories

If the target looks like a git URL or a shorthand, pcl performs a shallow clone (--depth 1 --filter=blob:none) into a temp directory, runs the scan, and cleans up. git must be on PATH.

Form Example
HTTPS https://github.com/psf/requests.git
SSH git@github.com:psf/requests.git
ssh:// ssh://git@github.com/psf/requests.git
git:// git://github.com/psf/requests
Shorthand github.com/psf/requests

Shorthand is also recognised for gitlab.com, bitbucket.org, and codeberg.org.

Counting rules

Each supported source file is parsed once via tree-sitter; every line is classified into exactly one bucket:

Bucket What it is
blank whitespace-only line
comment line inside a non-doc comment (e.g. # in Python, // and /* */ in JS/TS/Rust)
doc Python module/class/function docstrings, JSDoc /** */, Rust ///, //!, /** */, /*! */
code everything else

Resolution priority on overlap: comment > doc > blank > code. So a blank line inside a multi-line docstring counts as doc (it's part of the doc content), while a trailing # ... on a code line stays code.

Tree-sitter parses each file; comment-only and string-literal nodes are mapped to the right bucket per language. Strings containing // or # are never mistaken for comments. On parse errors tree-sitter's error recovery still surfaces well-formed regions.

Excludes

--exclude accepts one or more fnmatch patterns. They combine as a logical OR — a path is excluded if it matches any pattern.

Each pattern is tested two ways:

  1. against the full path relative to the scan root (e.g. src/migrations/*)
  2. against every individual path component, including the filename (e.g. tests matches any tests/ folder; *_test.py matches any matching file)

Hidden directories (starting with .) and __pycache__ are skipped by default.

Examples

# Folder names — match at any depth
pcl . --exclude tests
pcl . --exclude tests docs

# Anchor a folder pattern to a specific path
pcl . --exclude "src/migrations/*"

# Filename patterns
pcl . --exclude "test_*.py"            # test_foo.py
pcl . --exclude "*_test.py"            # fetch_orders_test.py
pcl . --exclude "*test*.py"            # anything with 'test' in the name

# Combine freely — folders, paths, and filenames at once
pcl . --exclude tests docs "src/migrations/*" "*_test.py" "test_*.py"

Tips

  • Quote glob patterns ("*_test.py") so the shell doesn't expand them against your current directory before pcl sees them.
  • --exclude is greedy (consumes all following words). Put the target before it, or separate with --:
    pcl /repo --exclude tests "*_test.py"           # target first  ✓
    pcl --exclude tests "*_test.py" -- /repo        # -- terminator ✓
    pcl --exclude tests "*_test.py" /repo           # /repo eaten   ✗
    

Flags

Flag Description
target folder, file, or git URL/shorthand. Defaults to .
--exclude PATTERN [PATTERN ...] fnmatch patterns to skip
--lang NAME [NAME ...] limit counting to the named languages (default: all supported)
--strip-comments exclude comment-only lines from the headline LOC total
-v, --version print the installed version and exit

--strip-comments only changes the headline number; the breakdown is always shown. It composes with everything else:

pcl github.com/psf/requests --exclude tests docs "*_test.py" --strip-comments

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

python_count_lines-0.1.1.tar.gz (47.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

python_count_lines-0.1.1-py3-none-any.whl (14.5 kB view details)

Uploaded Python 3

File details

Details for the file python_count_lines-0.1.1.tar.gz.

File metadata

  • Download URL: python_count_lines-0.1.1.tar.gz
  • Upload date:
  • Size: 47.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for python_count_lines-0.1.1.tar.gz
Algorithm Hash digest
SHA256 f8bf6c553a9a3b622f6dca8d5148a312d1377eca0b2c819ef8d1ca04d934c3a6
MD5 db6b7ab080b45f26e3b4fd1306957a81
BLAKE2b-256 c9633c146e253f03c7365eb1a88551a137782c067039ff5caebef07e45ceff2b

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_count_lines-0.1.1.tar.gz:

Publisher: publish.yml on frndvrgs/python-count-lines

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_count_lines-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for python_count_lines-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c6711adb5c141cd7df56bb2ddc17bb9098ac37cfe154cc9f987105452ffc4aab
MD5 09c000e87758fdf1d2e5cb5d2c665b7c
BLAKE2b-256 b6f3907bbfe36ddd5b94785352ff1cbe179e9d2170d3bc8b2d3b63c1a5f9e22f

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_count_lines-0.1.1-py3-none-any.whl:

Publisher: publish.yml on frndvrgs/python-count-lines

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page