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.2.tar.gz (48.4 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.2-py3-none-any.whl (14.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: python_count_lines-0.1.2.tar.gz
  • Upload date:
  • Size: 48.4 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.2.tar.gz
Algorithm Hash digest
SHA256 420c5c9ecb58e92bfda3a0883ebe85f95a45e188e18b7fdb9f556706326124ec
MD5 a4c069f2f0b736272996870e4763d3ca
BLAKE2b-256 190d697e68691c021824454aca1588db98edcea5fd513c3e88374aaf86c3d444

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_count_lines-0.1.2.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.2-py3-none-any.whl.

File metadata

File hashes

Hashes for python_count_lines-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 06dad44331fbac5f21c3c813aa8650f77bbc5f4a4b5cd9c120761f8960e4b64d
MD5 2bc17c8411b3c5c47bc10c7ce685c652
BLAKE2b-256 e98927bdd58c7e8fac5c0e8392d89de28c11ee3e40a53bb080991dad6c2b8d93

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_count_lines-0.1.2-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