Modules Vs Monolith? Custom App Design Question

Hi all.

I have been working with some amazing people to create a custom app for our services. Love how everything is structured.

I’m curious if someone knowledgeable can answer my question. Only interested in advice from people who really know the layout. Having done actual tests, have article support, or understand frappe’s architecture.

My Question
Does importing a bunch of modules(sales_invoice_handler.py) that service one doctype(doctype.py) affect performance?


My understanding is bench migrate builds python cache and that in theory, doctype.py that calls 15 other .py files only affects initial build time.

And if you’re like me and have a 3000 line doctype.py, you begin to pull your hair out and go crazy. My happy place is module layouts like below:

from .sales_invoice_handler import actual_function

class Doctype-Name(Document):
@frappe.whitelist()
    def actual_function_wrapper(self):
        return actual_function(self.name)

Then instead of 3000 lines, document.py is 200 lines of nice, clean wrappers. Plus just a bit more functionality more convenient to write in main py.

So again. Would someone be willing to speak to if having 15 modules(seperate py files) for a complex doctype affects performace overmuch more than a 3000 line doctype? My suspicion is I’m in the clear, but I’m not sure if I have considered everything.

I don’t think your account of what bench migrate does is quite right, but as to your broader question: when in doubt, test!

# module.py
def testfunc(length):
  return "-".join(str(n) for n in range(length))
# import_test.py
from module import testfunc
class ImportTest:
  def run(self):
    x = testfunc(100)
# inline_test.py
class InlineTest:
  def testfunc(self, length):
    return "-".join(str(n) for n in range(length))
  def run(self):
    x = self.testfunc(100)
# main.py
from timeit import timeit

reps = 1000000
a = timeit('ImportTest().run()', setup='from import_test import ImportTest', number=reps)
b = timeit('InlineTest().run()', setup='from inline_test import InlineTest', number=reps)

print(f"Import: {a}")
print(f"Inline: {b}")
(3.12.7)  > python main.py
Import: 7.467847415990545
Inline: 7.440962416003458

So…indeed…no meaningful difference.

2 Likes

Thank you! Yes, I meant bench restart.

Sure thing. It sounds like you’re misunderstanding bench’s role in this process (it’s not bench restart either), but as far as the runtime goes there’s not a significant performance difference.

1 Like