1

I'm currently implementing some System Checks of my own, and noticed that Django recommends using hardcoded error identifiers, like so:

        Error(
            'an error',
            hint='A hint.',
            obj=checked_object,
            id='myapp.E001',
        )

I thought that maybe this is merely serving tutorial purposes, but turns out that's actually how they do it.

From the same code above, I noticed that they use a prefix scheme for identifying the type of message that is being created (e.g. E for Error, W for Warning, C for Critical, etc).

But I don't understand the numbering system. You just enter any three digit number and that's it? What happens if there's a collision? Has anyone come across a better paradigm to manage the error codes?

André Fratelli
  • 5,920
  • 7
  • 46
  • 87
  • 1
    It is *per app* (the `myapp` prefix), so that means that as long as you distributed numbers for your app without overlap, it is fine. – Willem Van Onsem Dec 08 '20 at 20:58
  • 1
    Programmers define these code numbers ensuring that they do not clash, sometimes keeping ranges in between for later to group similar error types ( jumps by 100 etc.) – iklinac Dec 08 '20 at 21:03
  • So it's a manual process. This could very easily escape a merge review. – André Fratelli Dec 08 '20 at 21:34

1 Answers1

1

But I don't understand the numbering system. You just enter any three digit number and that's it? What happens if there's a collision?

No, you use the app name as prefix:

Error(
    'an error',
    hint='A hint.',
    obj=checked_object,
    id='myapp.E001',  # ← myapp prefix
)

as long as for an app there is no overlap, that is fine.

Strictly speaking you can make some sort of dispatcher that will generate new numbers, but that seems overkill. You could use something like:

from collections import defaultdict

def make_error_code(app_name, type):
    items = make_error_code.data[app_name]
    counter = items.get(type, 0) + 1
    items[type] = counter
    return f'{app_name}.{type}{counter:03}'

make_error_code.data = defaultdict(lambda: defaultdict(dict))

You can then make errors with the make_error_code:

Error(
    'an error',
    hint='A hint.',
    obj=checked_object,
    id=make_error_code('my_app', 'E')  # my_app.E001
),
Error(
    'an error',
    hint='A hint.',
    obj=checked_object,
    id=make_error_code('my_app', 'W')  # my_app.W001
),
Error(
    'an error',
    hint='A hint.',
    obj=checked_object,
    id=make_error_code('my_app', 'E')  # my_app.E002
)

In that cas you thus define the errors as constants where you construct the error once and then use that error in the functions you define.

But for error codes that are typically scoped per app, it looks like overkill.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • In case of any change to the flow of execution (say, an if statement), the codes are not generated in the proper order. I thought of this approach, TBH, but it makes the error codes undocumentable. – André Fratelli Dec 08 '20 at 21:33
  • @AndréFratelli: no not per se. You can make use of "reflection" to automatically document the errors. You simply look at the local variables that are instantiated and then automatically generate (HTML) documentation. – Willem Van Onsem Dec 08 '20 at 21:35
  • Also, I understand the app prefix, but I'm concerned about multiple people working on the same package. – André Fratelli Dec 08 '20 at 21:35
  • @AndréFratelli: well a professor once said to me that "coding skills are less important than people skills in software". If multiple people work on the same project, you need to communicate (daily standups, pull request reviews, etc.). – Willem Van Onsem Dec 08 '20 at 21:36
  • Hmm.. So the error the codes would be versioned. I'm not sure what to feel about that. For example, an error code can be used to google for a problem, and if it changes, it people's lives harder. It's common to stick with the error codes (e.g. globally defined somewhere, which is the approach I'm considering). – André Fratelli Dec 08 '20 at 21:38
  • @AndréFratelli: why would the error codes change: you simply define the errors in a single file, and only append to the file. You can make a simple pre-commit hook to enforce this. – Willem Van Onsem Dec 08 '20 at 21:39
  • Ah! It could go on a global file. That makes more sense. That way the order doesn't change. I was thinking of keeping manual track of the codes in a centralized way, you're suggesting the same but with a generator. Sounds worth considering. – André Fratelli Dec 08 '20 at 21:40
  • Thought of another problem with this. What if the file is loaded twice? – André Fratelli Dec 08 '20 at 22:11
  • @AndréFratelli: how would you load the file twice? If you import it a second time, it will not be loaded another time, then you get a reference to the module already loaded. – Willem Van Onsem Dec 08 '20 at 22:13
  • Not in that case, no. But I've seen a few examples. Calling `reload`, for example; although I think it would make sense in that case. I also saw this: https://stackoverflow.com/questions/4798589/what-could-cause-a-python-module-to-be-imported-twice – André Fratelli Dec 08 '20 at 22:18
  • I still have to test, but it seems like the `make_error_code` function would just be defined again as well and the errors registered in the same order, because the data was lost. It's not looking like an issue, actually. – André Fratelli Dec 08 '20 at 22:20