1

This answer contains the following line:

fcntl.lockf(fp, fcntl.LOCK_EX | fcntl.LOCK_NB)

The pipe, is in Python a "bitwise OR"

I have checked the Python docs for fcntl.lockf(fd, cmd, len=0, start=0, whence=0) and it says that cmd is one of:

LOCK_UN – unlock

LOCK_SH – acquire a shared lock

LOCK_EX – acquire an exclusive lock

Have also been reading about those variables here

Does anyone understand that line and what it does and if it would work with just one argument and no pipe symbol?

LOCK_NB is not even mentioned in the Python docs..

Community
  • 1
  • 1
cardamom
  • 6,873
  • 11
  • 48
  • 102
  • related to https://stackoverflow.com/questions/44115670/combine-bitflags, and any other question on SO dealing with combining bit flags – DeepSpace Jan 23 '19 at 16:04

1 Answers1

2

Those names are numeric constants, each a power of 2. That means they are integers with a single bit set.

You can combine such numbers with | to produce an integer with multiple bits set, each bit representing a specific option:

>>> import fcntl
>>> fcntl.LOCK_SH  # bit 1 set
1
>>> fcntl.LOCK_EX  # bit 2 set
2
>>> fcntl.LOCK_NB  # bit 3 set
4
>>> fcntl.LOCK_UN  # bit 4 set
8
>>> fcntl.LOCK_SH | fcntl.LOCK_UN  # combine bits 1 and 4
9
>>> format(fcntl.LOCK_SH | fcntl.LOCK_UN, '04b')
'1001'

This is a very common way of passing setting configuration in system calls. fctnl.lock() is one such example, but there are many more where the same technique applies. The os.open() call is another example, as are the re module flags.

It is important to realise that these are just integers. Instead of setting the flags with fcntl.LOCK_EX | fcntl.LOCK_NB (setting bits 2 and 3), you could just pass in 6:

fcntl.lockf(fp, 6)

and there would be no difference, as far as the lockf() function is concerned. The point of using named constants is that using these makes for self-documenting code.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Thanks had no idea. Checked in interpreter, `fcntl.LOCK_EX` is 2, `fcntl.LOCK_NB` is 4, `fcntl.LOCK_SH` is 1 and `(fcntl.LOCK_EX | fcntl.LOCK_NB)` is 6. Could you describe what effect changing the things in that line will do? eg what if you had just `fcntl.LOCK_EX` and left out the pipe and the second part? – cardamom Jan 23 '19 at 16:10
  • 1
    @cardamom: first of all, teh `fcntl.lock()` call doesn't care how you create the value; you can pass in `4` or `6`. You need to read the documentation to see what will happen. Not using `LOCK_NB` means the call *blocks*, it won't return until the lock has been established. If you do set that flag, then the call raises an `OSError` exception if the lock can't be established. – Martijn Pieters Jan 23 '19 at 16:15