2

Initially i thought to do something like:

#EXIT CODES
class ExitCode(object):
    (USERHOME_INVALID, \
    USERHOME_CANNOT_WRITE, \
    USERHOME_CANNOT_READ, \
    BASHRC_INVALID) = range(-1, -5, -1)

But than I've realized that I'll have to know exactly the total number of EXIT_CODES, so that I can pass it to the range() function. Let's suppose I'll have 87 (arbitrary) EXIT_CODES... I don't want to count to 87 (not that it's hard) but I am looking for a more elegant solution.

Any suggestions ?

EDIT: EXIT_CODE is a negative int that will be passed to sys.exit . Instead of writing the number I prefer to use some sort of constants (something like #defines or enums in C, or enums in Java).

Andrei Ciobanu
  • 12,500
  • 24
  • 85
  • 118
  • 2
    Just a note - you don't need the backslashes in parentheses. It doesn't really matter, but they're superfluous, and not really "Pythonic". – li.davidm Sep 16 '10 at 23:09

5 Answers5

2

Sounds like what you want is the Python equivalent of an enumeration in C# or other similar languages. How can I represent an 'Enum' in Python? provides several solutions, though they still require the number of items you have. EDIT: How can I represent an 'Enum' in Python? looks way better.

Or you could try something like this (probably not the best solution, though):

class _ExitCode:
    _exit_codes=["EXIT_CODE","EXIT_CODE_TWO"]
    def __getattr__(self, name):
        if name in _ExitCode._exit_codes:
            return -(_ExitCode._exit_codes.index(name)+1)
        raise AttributeError("Exit code %s not found" % name)

ExitCode=_ExitCode()
print ExitCode.EXIT_CODE #-1
Community
  • 1
  • 1
li.davidm
  • 11,736
  • 4
  • 29
  • 31
1

Maybe I don't understand the question, but why don't you simply make a dictionary of exit codes and implement the desired behaviour in a function?

EXIT_CODES = dict(SUCCESS=0,
                  USER_NAME_INVALID=-1,
                  OTHER_ERROR=-2)

def exit(code):
   try:
      return EXIT_CODES[code]  
   except KeyError:
      raise KeyError("exit code %s is not implemented" % code)

So you can use it like

# some computation goes here
return exit("SUCCESS")

And if you want to make "automatic" assignment of numbers (I don't recommend this) you can simply create a list of exit codes and return the negative of the index:

EXIT_CODES = ['SUCCESS', 'ERROR_1', 'ERROR_2']
return -EXIT_CODES.index('ERROR_1')
# will return -1

(for the last one, you can implement a function similar to the dictionary-based one)

Escualo
  • 40,844
  • 23
  • 87
  • 135
  • The problem is with auto incremenation of the values contained by the dict. I don't want to write 0, -1, -2, ..., -87 manually and then realize i have to regroup them. – Andrei Ciobanu Sep 17 '10 at 00:13
1

I must note that it's not at all certain a negative status makes sense for sys.exit(); at least on Linux, it will be interpreted as an unsigned 8-bit value (range 0-255). As for an enumerated type, it's possible to do something like:

class ExitStatus: pass
for code, name in enumerate("Success Failure CriticalFailure".split()):
    setattr(ExitStatus, name, code)

Resulting in something like:

>>> ExitStatus.__dict__
{'CriticalFailure': 2, 'Failure': 1, '__module__': '__main__',
'__doc__': None, 'Success': 0}

The predefined values in normal Unix systems are EXIT_FAILURE=1 and EXIT_SUCCESS=0.

Addendum: Considering the concern about IDE identification of identifiers, one could also do something like:

class EnumItem: pass
def adjustEnum(enum):
    value=0
    enumdict=enum.__dict__
    for k,v in enumdict.items():
        if isinstance(v,int):
            if v>=value:
                value=v+1
    for k,v in enumdict.items():
        if v is EnumItem:
            enumdict[k]=value
            value+=1

class ExitStatus:
    Success=0
    Failure=EnumItem
    CriticalFailure=EnumItem
adjustEnum(ExitStatus)

Second edit: Couldn't keep away. Here's a variant that assigns the values in the order you've written the names.

class EnumItem:
    serial=0
    def __init__(self):
        self.serial=self.__class__.serial
        self.__class__.serial+=1

def adjustEnum(enum):
    enumdict=enum.__dict__
    value=0
    unknowns={}
    for k,v in enumdict.items():
        if isinstance(v,int):
            if v>=value:
                value=v+1
        elif isinstance(v,EnumItem):
            unknowns[v.serial]=k
    for i,k in sorted(unknowns.items()):
        enumdict[k]=value
        value+=1
    return enum

@adjustEnum
class ExitStatus:
    Success=0
    Failure=EnumItem()
    CriticalFailure=EnumItem()

Obviously the growing complexity is inelegant, but it does work.

Yann Vernier
  • 15,414
  • 2
  • 28
  • 26
1

I guess I looked at this question earlier and didn't see it, but one obvious thing to do is use a dict.

def make_exit_codes(*exit_codes):
    return dict((name, -value - 1) for name, value in enumerate(exit_codes)) 

EXIT_CODES = make_exit_codes('USERHOME_INVALID', 'USERHOME_CANNOT_WRITE',
                             'USERHOME_CANNOT_READ', 'BASHRC_INVALID')
aaronasterling
  • 68,820
  • 20
  • 127
  • 125
0

You can create variables (or class attributes) on-the-fly with Python. For example

ExitCodes = '''USERHOME_INVALID, USERHOME_CANNOT_WRITE,
               USERHOME_CANNOT_READ, BASHRC_INVALID'''

for i, s in enumerate(ExitCodes.split(','), 1):
    exec('%s = %d' % (s.strip(), -i))

print USERHOME_INVALID
print USERHOME_CANNOT_WRITE
print USERHOME_CANNOT_READ
print BASHRC_INVALID

sys.exit(USERHOME_INVALID)

>>> -1
>>> -2
>>> -3
>>> -4
martineau
  • 119,623
  • 25
  • 170
  • 301
  • 2
    Please don't use things like exec() too eagerly. You could probably do just as well using `globals().__setitem__(s,-i)`. – Yann Vernier Sep 17 '10 at 07:05
  • 1
    this wouldn't have been worth a downvote if somebody else hadn't have upvoted it but using `exec` deserves to be at the bottom of the list here. l33tnerd's solution is much more elegant. – aaronasterling Sep 17 '10 at 09:09
  • "A foolish consistency is the hobgoblins of little minds..." Which means, I believe, decide for yourself on a case-by-case basis. – martineau Sep 17 '10 at 09:11
  • I actually like this solution. The drawback is that the exec'ed variables aren't recognized by the IDE I am currently using (Eclipse&PyDev), as they are declared at runtime. – Andrei Ciobanu Sep 18 '10 at 20:29
  • 1
    @Andrei Ciobanu. Never thought of that -- guess it would take a *very* smart Python-aware IDE to recognize identifiers created at runtime, and might not even be possible in some circumstances. However I suspect what you say would also be true for most of the answers currently posted. Thanks for pointing out another tradeoff to be considered. – martineau Sep 19 '10 at 07:49