You might consider using a mix-in.

class TestSkeleton:
    maxDiff = None
    databases = "__all__"

    def setUp(self):
        self.testStartTime = time.time()

    def tearDown(self):
        _reportRunTime(self.id(), self.testStartTime)

    def setUpClass(self):
        self.classStartTime = time.time()

    def setUpTestData(self):
        _reportRunTime(f"{self.__class__.__name__}.setUpTestData", self.classStartTime)

    class Meta:
        abstract = True


class TracebaseTestCase(TestSkeleton, TestCase):
    pass


class TracebaseTransactionTestCase(TestSkeleton, TransactionTestCase):
    pass

tearDown and setUpTestData probably need some adjustment depending on exactly what _reportRunTime is and what it's first argument is supposed to represent.

🌐
npm
npmjs.com › package › jscpd
jscpd - npm
3 weeks ago - 223 formats including: JavaScript, TypeScript, Python, Go, Rust, Java, C/C++, C#, Ruby, PHP, Swift, Kotlin, Scala, Vue SFC, Svelte, Astro, Markdown, SQL, HTML, CSS, Bash, Dart, Lua, R, Haskell, Clojure, Elixir, Apex, CFML, and 200+ more. Run jscpd --list for the full list, or see FORMATS.md.
      » npm install jscpd
    
Published   Jul 08, 2026
Version   5.0.12
🌐
jscpd
jscpd.dev
jscpd - Copy/Paste Detector for Source Code - jscpd
jscpd v5 is a Rust-powered rewrite that hunts down duplicated blocks across 223+ languages up to 37x faster than v4 — because life's too short to maintain the same bug in five different places.
🌐
GitHub
github.com › kucherenko › jscpd
GitHub - kucherenko/jscpd: Copy/paste detector for programming source code, supports 223 formats. AI-ready with token-efficient reporter, skill and MCP server. · GitHub
Copy/paste detector for programming source code, supports 223 formats. AI-ready with token-efficient reporter, skill and MCP server. - kucherenko/jscpd
Author   kucherenko
🌐
GitHub
github.com › elpassion › danger-py-jscpd
GitHub - elpassion/danger-py-jscpd: danger-python plugin for detecting copy/paste · GitHub
# install danger-js npm install -g danger # install jscpd npm install -g jscpd # install danger-python pip install danger-python # install danger-py-jscpd pip install danger-py-jscpd # modify dangerfile.py to include plugin danger_py_jscpd.jscpd() # run danger-python danger-python pr https://github.com/elpassion/danger-py-jscpd/pull/2
Author   elpassion
🌐
UNPKG
app.unpkg.com › jscpd@0.6.0 › files › README.md
jscpd
Copy/paste detector for programming code, support JavaScript, CoffeeScript, PHP, Ruby, Python, Less, Go, Java, Yaml, C#, C++, C languages ... # Copy/paste detector for programming source code. `jscpd` is a tool for detect copy/paste "design pattern" in programming source code.
🌐
MegaLinter
megalinter.io › latest › descriptors › copypaste_jscpd
jscpd configuration in MegaLinter - MegaLinter by OX Security
JSCPD (JavaScript Copy-Paste Detector) is a sophisticated copy-paste detection tool that scans codebases across multiple programming languages to identify duplicated code blocks. It helps maintain code quality by finding excessive code duplication ...
Top answer
1 of 2
2

You might consider using a mix-in.

class TestSkeleton:
    maxDiff = None
    databases = "__all__"

    def setUp(self):
        self.testStartTime = time.time()

    def tearDown(self):
        _reportRunTime(self.id(), self.testStartTime)

    def setUpClass(self):
        self.classStartTime = time.time()

    def setUpTestData(self):
        _reportRunTime(f"{self.__class__.__name__}.setUpTestData", self.classStartTime)

    class Meta:
        abstract = True


class TracebaseTestCase(TestSkeleton, TestCase):
    pass


class TracebaseTransactionTestCase(TestSkeleton, TransactionTestCase):
    pass

tearDown and setUpTestData probably need some adjustment depending on exactly what _reportRunTime is and what it's first argument is supposed to represent.

2 of 2
0

@chepner's answer is correct, though I didn't understand why or how it worked because I never understood the concept of mixins. I couldn't map it to my naive notion of multiple inheritance. So I did some testing with trial and error and now have more confidence that I have a better conceptual (though perhaps technically inaccurate) understanding of how they work. I had previously been conceptually dissuaded by the notion of "multiple inheritance" to imply multiple independent parent classes/objects. However (to use a geeky analogy) I now see it more like the parent being "Tuvix" from Star Trek Voyager. The parents (Tuvok and Neelix) are not independent individuals. There's only 1 parent: Tuvix, who is a merging of the 2 parents.

And from my testing, I have come to understand that the order of the superclasses establishes whose characteristics dominate. Precedence goes from left to right. Anything you "override" in the left-side class is what gets set/called when a derived class calls/gets it.

I don't need to reiterate @chepner's answer, but I will provide an example to demonstrate why it works... Take these classes as an example:

class mybaseclass():
    """a.k.a. TestCase"""

    classvar = "base"

    def member_override_test(self):
        print(f"member_override_test in mybaseclass, classvar: [{self.classvar}]")

    @classmethod
    def classmethod_override_test(cls):
        print(f"classmethod_override_test in mybaseclass, classvar: [{cls.classvar}]")

    def run_member_super_test(self):
        print(f"classvar: {self.classvar}")
        print("Calling member_override_test from mybaseclass:")
        self.member_override_test()
        print("Calling classmethod_override_test from mybaseclass:")
        self.classmethod_override_test()
        print("Calling run_test from mybaseclass:")
        self.run_test()


class mymixinclass():
    """a.k.a. TestSkeleton"""

    classvar = "mixin"

    def member_override_test(self):
        print(f"member_override_test in mymixinclass, classvar: [{self.classvar}]")

    @classmethod
    def classmethod_override_test(cls):
        print(f"classmethod_override_test in mymixinclass, classvar: [{cls.classvar}]")


class mypseudoderivedclass(mymixinclass, mybaseclass):
    """a.k.a. TracebaseTestCase"""

    def member_override_test(self):
        print(f"member_override_test in mypseudoderivedclass, classvar: [{self.classvar}]")
        print(f"Calling super.member_override_test")
        super().member_override_test()

    def classmethod_override_test(self):
        print(f"classmethod_override_test in mypseudoderivedclass, classvar: [{self.classvar}]")
        print(f"Calling super.classmethod_override_test")
        super().classmethod_override_test()

    def run_test(self):
        print(f"classvar: {self.classvar}")
        print("Calling member_override_test from mypseudoderivedclass object:")
        self.member_override_test()
        print("Calling classmethod_override_test from mypseudoderivedclass object:")
        self.classmethod_override_test()


class reversemypseudoderivedclass(mybaseclass, mymixinclass):
    """a.k.a. TracebaseTestCase - reversing the order of the mixins"""

    def member_override_test(self):
        print(f"member_override_test in reversemypseudoderivedclass, classvar: [{self.classvar}]")
        print(f"Calling super.member_override_test")
        super().member_override_test()

    def classmethod_override_test(self):
        print(f"classmethod_override_test in reversemypseudoderivedclass, classvar: [{self.classvar}]")
        print(f"Calling super.classmethod_override_test")
        super().classmethod_override_test()

    def run_test(self):
        print(f"classvar: {self.classvar}")
        print("Calling member_override_test from mypseudoderivedclass object:")
        self.member_override_test()
        print("Calling classmethod_override_test from mypseudoderivedclass object:")
        self.classmethod_override_test()

And here's what you see when you play with those classes in the python shell:

In [1]: from DataRepo.tests.tracebase_test_case import mypseudoderivedclass, reversemypseudoderivedclass
   ...: mpdc = mypseudoderivedclass()

In [2]: mpdc.run_test()
   ...: 
classvar: mixin
Calling member_override_test from mypseudoderivedclass object:
member_override_test in mypseudoderivedclass, classvar: [mixin]
Calling super.member_override_test
member_override_test in mymixinclass, classvar: [mixin]
Calling classmethod_override_test from mypseudoderivedclass object:
classmethod_override_test in mypseudoderivedclass, classvar: [mixin]
Calling super.classmethod_override_test
classmethod_override_test in mymixinclass, classvar: [mixin]

In [3]: mpdc.member_override_test()
member_override_test in mypseudoderivedclass, classvar: [mixin]
Calling super.member_override_test
member_override_test in mymixinclass, classvar: [mixin]

In [4]: mpdc.classmethod_override_test()
classmethod_override_test in mypseudoderivedclass, classvar: [mixin]
Calling super.classmethod_override_test
classmethod_override_test in mymixinclass, classvar: [mixin]

In [5]: mpdc.run_member_super_test()
classvar: mixin
Calling member_override_test from mybaseclass:
member_override_test in mypseudoderivedclass, classvar: [mixin]
Calling super.member_override_test
member_override_test in mymixinclass, classvar: [mixin]
Calling classmethod_override_test from mybaseclass:
classmethod_override_test in mypseudoderivedclass, classvar: [mixin]
Calling super.classmethod_override_test
classmethod_override_test in mymixinclass, classvar: [mixin]
Calling run_test from mybaseclass:
classvar: mixin
Calling member_override_test from mypseudoderivedclass object:
member_override_test in mypseudoderivedclass, classvar: [mixin]
Calling super.member_override_test
member_override_test in mymixinclass, classvar: [mixin]
Calling classmethod_override_test from mypseudoderivedclass object:
classmethod_override_test in mypseudoderivedclass, classvar: [mixin]
Calling super.classmethod_override_test
classmethod_override_test in mymixinclass, classvar: [mixin]

In [6]: rmpdc = reversemypseudoderivedclass()
   ...: rmpdc.run_test()
classvar: base
Calling member_override_test from mypseudoderivedclass object:
member_override_test in reversemypseudoderivedclass, classvar: [base]
Calling super.member_override_test
member_override_test in mybaseclass, classvar: [base]
Calling classmethod_override_test from mypseudoderivedclass object:
classmethod_override_test in reversemypseudoderivedclass, classvar: [base]
Calling super.classmethod_override_test
classmethod_override_test in mybaseclass, classvar: [base]

In [7]: rmpdc.member_override_test()
member_override_test in reversemypseudoderivedclass, classvar: [base]
Calling super.member_override_test
member_override_test in mybaseclass, classvar: [base]

In [8]: rmpdc.classmethod_override_test()
classmethod_override_test in reversemypseudoderivedclass, classvar: [base]
Calling super.classmethod_override_test
classmethod_override_test in mybaseclass, classvar: [base]

In [9]: rmpdc.run_member_super_test()
classvar: base
Calling member_override_test from mybaseclass:
member_override_test in reversemypseudoderivedclass, classvar: [base]
Calling super.member_override_test
member_override_test in mybaseclass, classvar: [base]
Calling classmethod_override_test from mybaseclass:
classmethod_override_test in reversemypseudoderivedclass, classvar: [base]
Calling super.classmethod_override_test
classmethod_override_test in mybaseclass, classvar: [base]
Calling run_test from mybaseclass:
classvar: base
Calling member_override_test from mypseudoderivedclass object:
member_override_test in reversemypseudoderivedclass, classvar: [base]
Calling super.member_override_test
member_override_test in mybaseclass, classvar: [base]
Calling classmethod_override_test from mypseudoderivedclass object:
classmethod_override_test in reversemypseudoderivedclass, classvar: [base]
Calling super.classmethod_override_test
classmethod_override_test in mybaseclass, classvar: [base]

Note that the class variable classvar's value and the method handling the (super) calls depend on the order of the mixins in the class's multiple inheritances.

Factory Alternative

Before I understood the mixins, I tried another solution that solves the sample problem in a different way. You can indeed parameterize the base class, which I learned from another answer, by creating a factory function, then vivify the derived classes by making factory calls.

Note, in this answer, I realized I could avoid overriding setUpClass by setting the "class start time" in the member data:

import time

from django.test import TestCase, TransactionTestCase

LONG_TEST_THRESH_SECS = 20
LONG_TEST_ALERT_STR = f" [ALERT > {LONG_TEST_THRESH_SECS}]"


def test_case_class_factory(base_class):
    class TracebaseTestCaseTemplate(base_class):
        maxDiff = None
        databases = "__all__"
        classStartTime = time.time()

        def setUp(self):
            self.testStartTime = time.time()

        def tearDown(self):
            reportRunTime(self.id(), self.testStartTime)

        @classmethod
        def setUpTestData(cls):
            super().setUpTestData()
            reportRunTime(f"{cls.__name__}.setUpTestData", cls.classStartTime)

        class Meta:
            abstract = True

    return TracebaseTestCaseTemplate


def reportRunTime(id, startTime):
    t = time.time() - startTime
    heads_up = ""  # String to include for tests that run too long

    if t > LONG_TEST_THRESH_SECS:
        heads_up = LONG_TEST_ALERT_STR

    print("TEST TIME%s: %s: %.3f" % (heads_up, id, t))


# Classes created by the factory with different base classes:
TracebaseTestCase = test_case_class_factory(TestCase)
TracebaseTransactionTestCase = test_case_class_factory(TransactionTestCase)

Both solutions work, and while I like the preserved inheritance of the factory method, once you understand mixins, I feel like @chepner's code is easier to read, so I will select his answer.

🌐
npm
npmjs.com › package › jscpd › v › 0.6.20
jscpd - Copy/paste detector for programming source code.
June 18, 2018 - Copy/paste detector for programming code, support JavaScript, CoffeeScript, PHP, Ruby, Python, Less, Go, Java, Yaml, C#, C++, C, Puppet, Twig languages. Latest version: 5.0.7, last published: 18 hours ago. Start using jscpd in your project by running `npm i jscpd`. There are 85 other projects in the npm registry using jscpd.
      » npm install jscpd
    
Published   Jun 11, 2026
Version   0.6.20
🌐
Codeac
codeac.io › documentation › jscpd.html
jscpd.json - Copy/Paste Detector configuration
Copy/Paste Detector (jscpd) helps you find duplicated code within your project which is a common technical debt.
Find elsewhere
🌐
PyPI
pypi.org › project › danger-py-jscpd
danger-py-jscpd · PyPI
February 14, 2020 - Python :: 3.8 · This version · 0.1.0 · Feb 14, 2020 · Download the file for your platform. If you're not sure which to choose, learn more about installing packages. danger-py-jscpd-0.1.0.tar.gz (3.0 kB view details) Uploaded Feb 14, 2020 Source · Filter files by name, interpreter, ABI, and platform.
      » pip install danger-py-jscpd
    
Published   Feb 14, 2020
Version   0.1.0
🌐
Reddit
reddit.com › r/coolgithubprojects › jscpd — copy-paste detector for 223 programming languages, with ci integration, html reports, and an ai-optimized output mode
r/coolgithubprojects on Reddit: jscpd — Copy-paste detector for 223 programming languages, with CI integration, HTML reports, and an AI-optimized output mode
May 19, 2026 -

Copy-paste is one of the most common sources of technical debt, and jscpd is the most language-comprehensive tool I've found for hunting it down.

What it does

Finds duplicated code blocks across your codebase using the Rabin-Karp algorithm. You point it at a directory, it tells you exactly where you (or your teammates) copy-pasted.

npx jscpd ./src

That's it. No install required.

Why it stands out

  • 223 supported formats — JS, TS, Python, Go, Rust, Java, C/C++, PHP, Ruby, Vue, Svelte, Astro, Terraform, SQL, Markdown, YAML... even Brainfuck and APL

  • Cross-file detection — a <script> block in a .vue file can match a .ts file

  • CI-friendly--threshold 5 fails the build if duplication exceeds 5%

  • Multiple reportershtml, json, xml, sarif (GitHub Code Scanning), markdown, csv

  • AI reporter — compact output with ~79% fewer tokens, designed for piping into LLM prompts

  • MCP server — works as a Model Context Protocol tool for AI assistants

  • Ignore blocks — wrap noisy code with /* jscpd:ignore-start */ comments

  • Git blame integration — find out who wrote the duplicated blocks

  • Self-dogfoods — the repo runs jscpd on itself in CI

Sample output (silent mode)

Found 60 exact clones with 3414 (46.81%) duplicated lines in 100 files.
Execution Time: 1381ms

Links

  • https://jscpd.dev

  • GitHub: https://github.com/kucherenko/jscpd

  • npm: https://www.npmjs.com/package/jscpd Used in GitHub Super Linter, Mega-Linter, and Codacy. MIT licensed.

🌐
PyPI
pypi.org › project › guardrails
guardrails · PyPI
[folder] # Fields under folder config are mandatory, if not provided, # will be considering the path of this ini file # Comma seperated source folders if more than one directory source_folder = .\EagleVision\eaglevision # Comma seperated test folders if more than one directory test_folder = .\EagleVision\test pytest_root = .\EagleVision\test report_folder = ..\opensource\python_guardrails\guardrails_report jscpd_root = .\EagleVision [python] python = python # path to the .pylintrc file if specific linting or leave empty after = pylint_rc_file = .\EagleVision\.pylintrc [coverage] # path to the
      » pip install guardrails
    
Published   Aug 30, 2021
Version   2.0.0
🌐
UNPKG
unpkg.com › browse › jscpd@0.6.0 › README.md
UNPKG
Copy/paste detector for programming code, support JavaScript, CoffeeScript, PHP, Ruby, Python, Less, Go, Java, Yaml, C#, C++, C languages ... # Copy/paste detector for programming source code. `jscpd` is a tool for detect copy/paste "design pattern" in programming source code.
🌐
Homebrew
formulae.brew.sh › formula › jscpd
Homebrew Formulae: jscpd
brew install jscpd · Copy/paste detector for programming source code · https://jscpd.dev/ License: MIT · Development: Pull requests · Formula JSON API: /api/formula/jscpd.json · Formula code: jscpd.rb on GitHub ·
🌐
jscpd
jscpd.dev › getting started › introduction
jscpd - Copy/Paste Detector
jscpd v5 is a complete Rust rewrite — a self-contained native binary that's 24-37x faster than the TypeScript engine, with no Node.js runtime required. It also introduces 13 reporters, OXC-based JS/TS tokenization, and parallel detection across ...
🌐
GitHub
github.com › codacy › codacy-duplication-jscpd
GitHub - codacy/codacy-duplication-jscpd: Codacy duplication tool for jscpd · GitHub
This is the duplication docker we use at Codacy to have jscpd support.
Forked by 2 users
Languages   Scala 82.5% | Dockerfile 17.5%
🌐
MegaLinter
megalinter.io › v5.0.2 › descriptors › copypaste_jscpd
jscpd - Mega-Linter
filtering can not be done using Mega-Linter configuration variables,it must be done using jscpd configuration or ignore file (if existing)
🌐
Tiagomssantos
tiagomssantos.github.io › MobileRT › jscpd-report › jscpd-report
jscpd, Copy/Paste Detector
dir: '${{ github.workspace }}' install-deps: 'true' modules: '' cache: 'true' cache-key-prefix: '${{ runner.os }}' setup-python: 'false' set-env: 'true' tools-only: 'false' aqtversion: '==2.1.*' py7zrversion: '==0.19.*' extra: '--external 7z' - name: Check Qt path installation.
🌐
GitHub
github.com › marketplace › actions › pull-requests-jscpd
Actions · GitHub Marketplace - pull-requests-jscpd
You can instal jscpd in local and run it in your repository to find all duplicates.
🌐
Githubhelp
githubhelp.com › sjsoad › jscpd
The jscpd from sjsoad - GithubHelp
jscpd --path my_project/ --languages javascript,coffee jscpd -f "**/*.js" -e "**/node_modules/**" jscpd --files "**/*.js" --exclude "**/*.min.js" --output report.xml jscpd --files "**/*.js" --exclude "**/*.min.js" --reporter json --output report.json jscpd --languages-exts javascript:es5,es6,es7,js;php:php5 jscpd --config test/.cpd.yaml ... #.cpd.yaml languages: - javascript - coffeescript - typescript - php - python - jsx - haxe - yaml - css - ruby - go - swift - twig - java - clike # c++, c, objective-c source - csharp # c# source - htmlmixed # html mixed source like knockout.js templates files: - "test/**/*" exclude: - "**/*.min.js" - "**/*.mm.js" reporter: json languages-exts: coffeescript: coffeee javascript: es es5 es6 es7