Skip to content

ADR-0011: camelCase API Convention

ADR-0011: camelCase API Convention

Status

Accepted

Context

Python uses snake_case for function names (e.g., lru_cache, zip_longest, ascii_lowercase), while JavaScript/TypeScript conventions use camelCase (e.g., lruCache, zipLongest, asciiLowercase).

When building a Python standard library for TypeScript developers, we face a design choice:

  1. Keep Python naming — familiar for Python developers, but feels foreign in TypeScript
  2. Use TypeScript naming — idiomatic for TypeScript, but requires learning new names

Decision

We adopt camelCase naming throughout pythonlib, providing a truly TypeScript-native experience:

Naming Transformations

Python (snake_case)TypeScript (camelCase)
lru_cachelruCache
zip_longestzipLongest
cache_infocacheInfo
total_secondstotalSeconds
ascii_lowercaseasciiLowercase
startswithstartsWith
groupdictgroupDict

Implementation

  1. Central Name Mapping: A name-mappings.ts file provides the canonical Python→JS name mapping:

    export const PYTHON_TO_JS_NAMES: Record<string, string> = {
    lru_cache: "lruCache",
    zip_longest: "zipLongest"
    // ...
    }
    export function toJsName(pythonName: string): string {
    return PYTHON_TO_JS_NAMES[pythonName] ?? pythonName
    }
  2. Transformer Integration: The transpiler automatically converts Python snake_case calls to JavaScript camelCase:

    # Python input
    from functools import lru_cache
    @lru_cache
    def fib(n): return n
    // TypeScript output
    import { lruCache } from "pythonlib/functools"
    const fib = lruCache((n) => n)

Complete Mapping by Module

functools:

  • lru_cachelruCache, cache_infocacheInfo, cache_clearcacheClear
  • partial_methodpartialMethod, single_dispatchsingleDispatch
  • attr_getterattrGetter, item_getteritemGetter, method_callermethodCaller
  • cmp_to_keycmpToKey, total_orderingtotalOrdering

itertools:

  • zip_longestzipLongest, takewhiletakeWhile, dropwhiledropWhile
  • filterfalsefilterFalse, combinations_with_replacementcombinationsWithReplacement

collections:

  • appendleftappendLeft, popleftpopLeft, extendleftextendLeft

datetime:

  • total_secondstotalSeconds, isoformatisoFormat
  • fromisoformatfromIsoFormat, fromtimestampfromTimestamp
  • isoweekdayisoWeekday, isocalendarisoCalendar

random:

  • randintrandInt, randrangerandRange
  • betavariatebetaVariate, expovariateexpoVariate (and other *variate functions)

string:

  • ascii_lowercaseasciiLowercase, ascii_uppercaseasciiUppercase
  • startswithstartsWith, endswithendsWith
  • safe_substitutesafeSubstitute, get_identifiersgetIdentifiers

os:

  • getcwdgetCwd, splitextsplitExt, normpathnormPath
  • isabsisAbs, relpathrelPath, commonpathcommonPath

re:

  • fullmatchfullMatch, findallfindAll, finditerfindIter
  • groupdictgroupDict

json:

  • sort_keyssortKeys, ensure_asciiensureAscii

dict/set:

  • setdefaultsetDefault, popitempopItem, fromkeysfromKeys
  • issubsetisSubset, issupersetisSuperset, isdisjointisDisjoint

core:

  • floordivfloorDiv, divmoddivMod

Consequences

Positive

  • Idiomatic TypeScript: Code feels native to TypeScript developers
  • IDE Support: Better autocomplete with standard JS naming conventions
  • Consistency: All pythonlib APIs follow the same camelCase pattern
  • Marketing: “Python’s powerful APIs, TypeScript’s familiar style”

Negative

  • Learning Curve for Python Developers: Must learn new names initially
  • Breaking Change: Existing code using snake_case must be updated

Example

Python:

from functools import lru_cache
from itertools import zip_longest
@lru_cache(maxsize=128)
def process(items):
return list(zip_longest(items, fillvalue=0))

TypeScript:

import { lruCache } from "pythonlib/functools"
import { zipLongest } from "pythonlib/itertools"
const process = lruCache(
(items) => {
return zipLongest(items, { fillvalue: 0 })
},
{ maxsize: 128 }
)