New Upstream Snapshot - python-sysv-ipc

Ready changes

Summary

Merged new upstream version: 1.0.0+git20210117.3.d8b463a (was: 1.0.0+git20210117.2.d8b463a).

Resulting package

Built on 2022-12-30T17:05 (took 7m50s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-snapshots python3-sysv-ipc-dbgsymapt install -t fresh-snapshots python3-sysv-ipc

Lintian Result

Diff

diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index c5bf870..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,12 +0,0 @@
-*.pyc
-.DS_Store
-releases/*
-build/*
-dist/*
-probe_results.h
-prober/foo
-prober/0.c
-prober/1.c
-prober/2.c
-notes.txt
-sysv_ipc.egg-info/*
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..ca035ff
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,37 @@
+Metadata-Version: 2.1
+Name: sysv_ipc
+Version: 1.1.0
+Summary: System V IPC primitives (semaphores, shared memory and message queues) for Python
+Home-page: http://semanchuk.com/philip/sysv_ipc/
+Download-URL: http://semanchuk.com/philip/sysv_ipc/sysv_ipc-1.1.0.tar.gz
+Author: Philip Semanchuk
+Author-email: philip@semanchuk.com
+Maintainer: Philip Semanchuk
+License: http://creativecommons.org/licenses/BSD/
+Keywords: sysv ipc inter-process communication semaphore shared memory shm message queue
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Operating System :: POSIX :: BSD :: FreeBSD
+Classifier: Operating System :: POSIX :: Linux
+Classifier: Operating System :: POSIX :: SunOS/Solaris
+Classifier: Operating System :: POSIX
+Classifier: Operating System :: Unix
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Utilities
+License-File: LICENSE
+
+Sysv_ipc gives Python programs access to System V semaphores, shared memory 
+and message queues. Most (all?) Unixes (including OS X) support System V IPC. 
+Windows+Cygwin 1.7 might also work. 
+
+Sample code is included.
+
+sysv_ipc is free software (free as in speech and free as in beer) released
+under a 3-clause BSD license. Complete licensing information is available in 
+the LICENSE file.
+
+You might also be interested in the similar POSIX IPC module at:
+http://semanchuk.com/philip/posix_ipc/
diff --git a/ReadMe.html b/ReadMe.html
index a1736f7..01c7232 100644
--- a/ReadMe.html
+++ b/ReadMe.html
@@ -1,16 +1,16 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<!DOCTYPE html>
 
-<html>
+<html lang='en'>
 
 <head>
 	<meta charset="utf-8">
 	<meta name="author" content="Philip Semanchuk">
-	<meta name="copyright" content="All contents &copy; 2018 Philip Semanchuk">
+	<meta name="copyright" content="All contents &copy; 2021 Philip Semanchuk">
 	<meta name="keywords" content="python sysv system v ipc semaphore shared memory message queue">
 
     <title>System V IPC for Python - Semaphores, Shared Memory and Message Queues</title>
 
-    <style type="text/css">
+    <style>
         dt {
             font-family: monospace;
             font-weight: bold;
@@ -45,12 +45,7 @@
 
 <h2>System V IPC for Python - Semaphores, Shared Memory and Message Queues</h2>
 
-<div class="rss">
-    <a href="rss.xml"><img src="/common/images/rss.png" width="28" height="28" alt=""></a>
-    <br><a href="rss.xml">RSS</a>
-</div>
-
-<p>This describes the <tt>sysv_ipc</tt> module which gives Python access
+<p>This describes the <code>sysv_ipc</code> module which gives Python access
 to System V inter-process semaphores, shared memory and message queues
 on most (all?) *nix flavors. Examples include macOS/OS X, Linux, FreeBSD,
 OpenSolaris 2008.11, HPUX, and AIX.
@@ -58,17 +53,16 @@ It might also work under Windows with a library like
 <a href="http://www.cygwin.com/">Cygwin</a>.
 </p>
 
-<p>It works with Python 2.7 and 3.x.
-It's released
+<p>It works with Python ≥ 3.6. It's released
 under a <a href="http://creativecommons.org/licenses/BSD/">BSD license</a>.
 </p>
 
 <p>You can <strong>download
-<a href="sysv_ipc-1.0.0.tar.gz">sysv_ipc version 1.0.0</a>
+<a href="sysv_ipc-1.1.0.tar.gz">sysv_ipc version 1.1.0</a>
 </strong>
-(<a href="sysv_ipc-1.0.0.md5.txt">[md5 sum]</a>,
-<a href="sysv_ipc-1.0.0.sha1.txt">[sha1 sum]</a>,
-<a href="sysv_ipc-1.0.0.sha256.txt">[sha256 sum]</a>)
+(<a href="sysv_ipc-1.1.0.md5.txt">[md5 sum]</a>,
+<a href="sysv_ipc-1.1.0.sha1.txt">[sha1 sum]</a>,
+<a href="sysv_ipc-1.1.0.sha256.txt">[sha256 sum]</a>)
 which contains the source code, setup.py, installation instructions and
 <a href="#samples">sample code</a>. You can read about
 <a href="history.html#current">all of the changes in this version</a>.
@@ -80,13 +74,13 @@ about some <a href="#bugs">known bugs</a>.
 </p>
 
 <p>You might be interested in the very similar module
-<a href="/philip/posix_ipc/"><tt>posix_ipc</tt></a>
+<a href="/philip/posix_ipc/"><code>posix_ipc</code></a>
 which provides Python access to POSIX IPC primitives. POSIX IPC is a little
 easier to use than SysV IPC, but not all operating systems support it
 completely.
 </p>
 
-<h2>Module <tt>sysv_ipc</tt></h2>
+<h2>Module <code>sysv_ipc</code></h2>
 
 <p>Jump to <a href="#semaphore">semaphores</a>,
 <a href="#shared_memory">shared memory</a>, or
@@ -97,10 +91,10 @@ completely.
 
 <dl>
     <dt>attach(id, [address = None, [flags = 0]])</dt>
-    <dd>Attaches the (existing) shared memory that has the given <tt>id</tt> and
+    <dd>Attaches the (existing) shared memory that has the given <code>id</code> and
     	returns a new SharedMemory object. See
     	<a href="#attach">SharedMemory.attach()</a> for details on the
-    	<tt>address</tt> and <tt>flags</tt> parameters.
+    	<code>address</code> and <code>flags</code> parameters.
 
     <p>This method is useful only under fairly unusual circumstances.
     	You probably don't need it.
@@ -108,20 +102,20 @@ completely.
     </dd>
 
     <dt>ftok(path, id, [silence_warning = False])</dt>
-    <dd>Calls <tt>ftok(path, id)</tt>. Note that
-    <a href="#ftok_weakness"><tt>ftok()</tt> has limitations</a>, and this
+    <dd>Calls <code>ftok(path, id)</code>. Note that
+    <a href="#ftok_weakness"><code>ftok()</code> has limitations</a>, and this
     function will issue a warning to that effect unless
-    <tt>silence_warning</tt> is True.
+    <code>silence_warning</code> is True.
     </dd>
 
     <dt>remove_semaphore(id)</dt>
-    <dd>Removes the semaphore with the given <tt>id</tt>.</dd>
+    <dd>Removes the semaphore with the given <code>id</code>.</dd>
 
     <dt>remove_shared_memory(id)</dt>
-    <dd>Removes the shared memory with the given <tt>id</tt>.</dd>
+    <dd>Removes the shared memory with the given <code>id</code>.</dd>
 
     <dt>remove_message_queue(id)</dt>
-    <dd>Removes the message queue with the given <tt>id</tt>.</dd>
+    <dd>Removes the message queue with the given <code>id</code>.</dd>
 </dl>
 
 
@@ -129,33 +123,33 @@ completely.
 
 <dl>
     <dt>IPC_CREAT, IPC_EXCL and IPC_CREX</dt>
-    <dd><tt>IPC_CREAT</tt> and <tt>IPC_EXCL</tt> are flags used when
+    <dd><code>IPC_CREAT</code> and <code>IPC_EXCL</code> are flags used when
         creating IPC objects. They're
-        bitwise unique and can be ORed together. <tt>IPC_CREX</tt> is
-        shorthand for <tt>IPC_CREAT | IPC_EXCL</tt>.
+        bitwise unique and can be ORed together. <code>IPC_CREX</code> is
+        shorthand for <code>IPC_CREAT | IPC_EXCL</code>.
 
-        <p>When passed to an IPC object's constructor, <tt>IPC_CREAT</tt> indicates
+        <p>When passed to an IPC object's constructor, <code>IPC_CREAT</code> indicates
             that you want to create a new object or open an existing one. If you want
             the call to fail if an object with that key already exists, specify
-            the  <tt>IPC_EXCL</tt> flag, too.
+            the  <code>IPC_EXCL</code> flag, too.
         </p>
     </dd>
 
     <dt>IPC_PRIVATE</dt>
     <dd>This is a special value that can be passed in place of a key. It implies that
         the IPC object should be available only to the creating process or its
-        child processes (e.g. those created with <tt>fork()</tt>).
+        child processes (e.g. those created with <code>fork()</code>).
     </dd>
 
     <dt>KEY_MIN and KEY_MAX</dt>
     <dd>Denote the range of keys that this module accepts. Your OS might
         limit keys to a smaller range depending on the typedef of
-        <tt>key_t</tt>.
+        <code>key_t</code>.
 
         <p>Keys randomly generated by this module are in the range
-        	<tt>1 ≤ key ≤ SHRT_MAX</tt>.
+        	<code>1 ≤ key ≤ SHRT_MAX</code>.
            That's type-safe unless your OS has a very bizarre
-           definition of <tt>key_t</tt>.
+           definition of <code>key_t</code>.
         </p>
     </dd>
 
@@ -172,28 +166,28 @@ completely.
     <dd>True if the platform supports timed semaphore waits, False otherwise.</dd>
 
     <dt>SHM_RDONLY</dt>
-    <dd>Pass this flag to <tt>SharedMemory.attach()</tt> to attach the segment
+    <dd>Pass this flag to <code>SharedMemory.attach()</code> to attach the segment
     read-only.</dd>
 
     <dt>SHM_RND</dt>
     <dd>You probably don't need this, but it can be used when attaching shared
         memory to force the address to be
-        rounded down to SHMLBA. See your system's man page for <tt>shmat()</tt>
+        rounded down to SHMLBA. See your system's man page for <code>shmat()</code>
         for more information.
     </dd>
 
     <dt>SHM_HUGETLB, SHM_NORESERVE and SHM_REMAP</dt>
     <dd>You probably don't need these. They're Linux-specific flags that can
         be passed to the SharedMemory
-        constructor, or to the <tt>.attach()</tt> function in the case of
-        SHM_REMAP. See your system's man page for <tt>shmget()</tt>
-        and <tt>shmat()</tt> for more information.
+        constructor, or to the <code>.attach()</code> function in the case of
+        SHM_REMAP. See your system's man page for <code>shmget()</code>
+        and <code>shmat()</code> for more information.
     </dd>
 </dl>
 
 <h3>Module Errors</h3>
 
-<p>In addition to standard Python errors (e.g. <tt>ValueError</tt>),
+<p>In addition to standard Python errors (e.g. <code>ValueError</code>),
 this module raises custom errors. These errors cover
 situations specific to IPC.
 </p>
@@ -221,8 +215,8 @@ situations specific to IPC.
     </dd>
 
     <dt>BusyError</dt>
-    <dd>Raised when a semaphore call to <tt>.P()</tt> or <tt>.Z()</tt> either times out
-        or would be forced to wait when its <tt>block</tt> attribute is False.
+    <dd>Raised when a semaphore call to <code>.P()</code> or <code>.Z()</code> either times out
+        or would be forced to wait when its <code>block</code> attribute is False.
     </dd>
 
     <dt>NotAttachedError</dt>
@@ -242,10 +236,10 @@ situations specific to IPC.
     <dt>Semaphore(key, [flags = 0, [mode = 0600, [initial_value = 0]]])</dt>
     <dd>Creates a new semaphore or opens an existing one.
 
-        <p><span class="param">key</span> must be <tt>None</tt>,
-            <tt>IPC_PRIVATE</tt> or
-        an integer &gt; <tt>KEY_MIN</tt> and &le; <tt>KEY_MAX</tt>. If the key
-        is <tt>None</tt>, the module chooses a random unused key.
+        <p><span class="param">key</span> must be <code>None</code>,
+            <code>IPC_PRIVATE</code> or
+        an integer &gt; <code>KEY_MIN</code> and &le; <code>KEY_MAX</code>. If the key
+        is <code>None</code>, the module chooses a random unused key.
         </p>
 
         <p>The <span class="param">flags</span> specify whether you want to create a
@@ -253,24 +247,24 @@ situations specific to IPC.
         </p>
 
         <ul>
-            <li>With <span class="param">flags</span> set to the <strong>default</strong> of <tt>0</tt>, the module attempts
+            <li>With <span class="param">flags</span> set to the <strong>default</strong> of <code>0</code>, the module attempts
                 to <strong>open an existing</strong> semaphore identified by <span class="param">key</span> and raises
-                a <tt>ExistentialError</tt> if that semaphore doesn't exist.
+                a <code>ExistentialError</code> if that semaphore doesn't exist.
             </li>
 
-            <li>With <span class="param">flags</span> set to <tt>IPC_CREAT</tt>, the module
+            <li>With <span class="param">flags</span> set to <code>IPC_CREAT</code>, the module
                 <strong>opens</strong> the semaphore identified by
                 <span class="param">key</span> <strong>or creates</strong> a new
-                one if no such semaphore exists. Using <tt>IPC_CREAT</tt> by itself
+                one if no such semaphore exists. Using <code>IPC_CREAT</code> by itself
                 is not recommended. (See <a href="#sem_init">Semaphore Initialization</a>.)
             </li>
 
             <li>With <span class="param">flags</span> set to
-                <tt>IPC_CREX</tt> (<tt>IPC_CREAT | IPC_EXCL</tt>),
+                <code>IPC_CREX</code> (<code>IPC_CREAT | IPC_EXCL</code>),
                 the module
                 <strong>creates a new semaphore</strong> identified by <span class="param">key</span>. If a
                 semaphore with that key already exists, the call raises an
-                <tt>ExistentialError</tt>.
+                <code>ExistentialError</code>.
                 <strong>The <span class="param">initial_value</span> is ignored unless
                     both of these flags are specified</strong> or
                     if the semaphore is read-only.
@@ -299,7 +293,7 @@ situations specific to IPC.
             </li>
 
             <li>When <span class="param">timeout</span> is 0, the call
-                raises a <tt>BusyError</tt> if it can't immediately
+                raises a <code>BusyError</code> if it can't immediately
                 acquire the semaphore. Since it will
                 return immediately if <em>not</em> asked to wait, this can be
                 thought of as "non-blocking" mode.
@@ -308,25 +302,25 @@ situations specific to IPC.
             <li>When the <span class="param">timeout</span> is &gt; 0, the call
                 will wait no longer than <span class="param">timeout</span>
                 seconds before either returning (having acquired the semaphore)
-                or raising a <tt>BusyError</tt>.
+                or raising a <code>BusyError</code>.
             </li>
         </ul>
 
         <p>When the call returns, the semaphore's value decreases by
             <span class="param">delta</span>
-            (or more precisely, <tt>abs(<span class="param">delta</span>)</tt>)
+            (or more precisely, <code>abs(<span class="param">delta</span>)</code>)
             which defaults to 1.
         </p>
 
-        <p>On platforms that don't support the <tt>semtimedop()</tt> API call,
+        <p>On platforms that don't support the <code>semtimedop()</code> API call,
             all timeouts (including zero) are treated as infinite. The call
             will not return until its wait condition is satisfied.
         </p>
 
-        <p>Most platforms provide <tt>semtimedop()</tt>. macOS is a
+        <p>Most platforms provide <code>semtimedop()</code>. macOS is a
             notable exception. The module's Boolean constant
-            <tt>SEMAPHORE_TIMEOUT_SUPPORTED</tt>
-            is True on platforms that support <tt>semtimedop()</tt>.
+            <code>SEMAPHORE_TIMEOUT_SUPPORTED</code>
+            is True on platforms that support <code>semtimedop()</code>.
         </p>
     </dd>
 
@@ -336,13 +330,13 @@ situations specific to IPC.
         Releases (increments) the semaphore.
 
         <p>The semaphore's value increases by <span class="param">delta</span>
-            (or more precisely, <tt>abs(<span class="param">delta</span>)</tt>)
+            (or more precisely, <code>abs(<span class="param">delta</span>)</code>)
             which defaults to 1.
         </p>
     </dd>
 
     <dt>P()</dt>
-    <dd>A synonym for <tt>.acquire()</tt> that takes the same parameters.
+    <dd>A synonym for <code>.acquire()</code> that takes the same parameters.
 
         <p>"P" stands for
         <span lang="nl">prolaag</span> or <span lang="nl">probeer te verlagen</span>
@@ -352,7 +346,7 @@ situations specific to IPC.
     </dd>
 
     <dt>V()</dt>
-    <dd>A synonym for <tt>.release()</tt> that takes the same parameters.
+    <dd>A synonym for <code>.release()</code> that takes the same parameters.
 
         <p>"V" stands for
         <span lang="nl">verhoog</span> (increase), the original name given by
@@ -364,7 +358,7 @@ situations specific to IPC.
     <dd>Blocks until zee zemaphore is zero.
 
         <p><span class="param">Timeout</span> has
-            the same meaning as described in <tt>.acquire()</tt>.
+            the same meaning as described in <code>.acquire()</code>.
         </p>
     </dd>
 
@@ -374,7 +368,7 @@ situations specific to IPC.
 
         <p>As far as I can tell, the effect of deleting a semaphore that
             other processes are still using is OS-dependent. Check your system's
-            man pages for <tt>semctl(IPC_RMID)</tt>.
+            man pages for <code>semctl(IPC_RMID)</code>.
         </p>
     </dd>
 </dl>
@@ -406,8 +400,8 @@ situations specific to IPC.
 
     <dt>block</dt>
     <dd>
-        Defaults to True, which means that calls to <tt>acquire()</tt> and
-        <tt>release()</tt> will not return
+        Defaults to True, which means that calls to <code>acquire()</code> and
+        <code>release()</code> will not return
         until their wait conditions are satisfied.
 
         <p>When False, these calls
@@ -421,7 +415,7 @@ situations specific to IPC.
 
         <p>Tip: the following Python code will display
         the mode in octal:<br>
-        <tt>print int(str(my_sem.mode), 8)</tt>
+        <code>print int(str(my_sem.mode), 8)</code>
         </p>
     </dd>
 
@@ -438,8 +432,8 @@ situations specific to IPC.
     <dd>The semaphore creator's group id.</dd>
 
     <dt>last_pid (read-only)</dt>
-    <dd>The PID of the process that last called <tt>semop()</tt> (<tt>.P()</tt>,
-        <tt>.V()</tt> or <tt>.Z()</tt>) on this semaphore.
+    <dd>The PID of the process that last called <code>semop()</code> (<code>.P()</code>,
+        <code>.V()</code> or <code>.Z()</code>) on this semaphore.
 
         <p>Linux and macOS also set this when the semaphore's value is changed,
         	although doing so disagrees with the POSIX specification.
@@ -449,23 +443,23 @@ situations specific to IPC.
 
     <dt>waiting_for_nonzero (read-only)</dt>
     <dd>The number of processes waiting for the value of the semaphore to become
-        non-zero (i.e. the number waiting in a call to <tt>.P()</tt>).
+        non-zero (i.e. the number waiting in a call to <code>.P()</code>).
     </dd>
 
     <dt>waiting_for_zero (read-only)</dt>
     <dd>The number of processes waiting for the value of the semaphore to become
-        zero (i.e. the number waiting in a call to <tt>.Z()</tt>).
+        zero (i.e. the number waiting in a call to <code>.Z()</code>).
     </dd>
 
     <dt>o_time (read-only)</dt>
-    <dd>The last time <tt>semop()</tt> (i.e. <tt>.P()</tt>, <tt>.V()</tt> or
-        <tt>.Z()</tt>) was called on this semaphore.
+    <dd>The last time <code>semop()</code> (i.e. <code>.P()</code>, <code>.V()</code> or
+        <code>.Z()</code>) was called on this semaphore.
     </dd>
 </dl>
 
 <h4>Context Manager Support</h4>
 
-<p>These semaphores provide <tt>__enter__()</tt> and <tt>__exit__()</tt>
+<p>These semaphores provide <code>__enter__()</code> and <code>__exit__()</code>
 methods so they can be used in context managers. For instance --
 </p>
 
@@ -475,7 +469,7 @@ with sysv_ipc.Semaphore(name) as sem:
 </pre>
 
 <p>Entering the context acquires the semaphore, exiting the context releases
-	the semaphore. See <tt>demo4/child.py</tt> for a complete example.
+	the semaphore. See <code>demo4/child.py</code> for a complete example.
 </p>
 
 
@@ -487,10 +481,10 @@ with sysv_ipc.Semaphore(name) as sem:
 
 <p>
 	In addition to the
-	methods and attributes described below, <tt>SharedMemory</tt> supports
-	Python's buffer protocol, which means you can create <tt>bytearray</tt>
-	and <tt>memoryview</tt> objects based on a <tt>SharedMemory</tt> segment.
-	See <tt>demos/buffer_protocol</tt> for an example.
+	methods and attributes described below, <code>SharedMemory</code> supports
+	Python's buffer protocol, which means you can create <code>bytearray</code>
+	and <code>memoryview</code> objects based on a <code>SharedMemory</code> segment.
+	See <code>demos/buffer_protocol</code> for an example.
 </p>
 
 <h4>Methods</h4>
@@ -500,10 +494,10 @@ with sysv_ipc.Semaphore(name) as sem:
     <dd>Creates a new shared memory segment or opens an existing one.
         The memory is automatically attached.
 
-        <p><span class="param">key</span> must be <tt>None</tt>,
-            <tt>IPC_PRIVATE</tt> or
-        an integer &gt; <tt>0</tt> and &le; <tt>KEY_MAX</tt>. If the key
-        is <tt>None</tt>, the module chooses a random unused key.
+        <p><span class="param">key</span> must be <code>None</code>,
+            <code>IPC_PRIVATE</code> or
+        an integer &gt; <code>0</code> and &le; <code>KEY_MAX</code>. If the key
+        is <code>None</code>, the module chooses a random unused key.
         </p>
 
         <p>The <span class="param">flags</span> specify whether you want to create a
@@ -512,29 +506,29 @@ with sysv_ipc.Semaphore(name) as sem:
 
         <ul>
             <li>With <span class="param">flags</span> set to the
-                <strong>default</strong> of <tt>0</tt>, the module attempts
+                <strong>default</strong> of <code>0</code>, the module attempts
                 to <strong>open an existing</strong> shared memory segment identified by
                 <span class="param">key</span> and raises
-                a <tt>ExistentialError</tt> if it doesn't exist.
+                a <code>ExistentialError</code> if it doesn't exist.
             </li>
 
-            <li>With <span class="param">flags</span> set to <strong><tt>IPC_CREAT</tt></strong>, the module
+            <li>With <span class="param">flags</span> set to <strong><code>IPC_CREAT</code></strong>, the module
                 <strong>opens</strong> the shared memory segment identified
                 by <span class="param">key</span> <strong>or
                 creates</strong> a new one if no such segment exists.
-                Using <tt>IPC_CREAT</tt> by itself
+                Using <code>IPC_CREAT</code> by itself
                 is not recommended. (See <a href="#mem_init">Memory Initialization</a>.)
             </li>
 
             <li>With <span class="param">flags</span> set to
-                <strong><tt>IPC_CREX</tt></strong> (<tt>IPC_CREAT | IPC_EXCL</tt>),
+                <strong><code>IPC_CREX</code></strong> (<code>IPC_CREAT | IPC_EXCL</code>),
                 the module
                 <strong>creates</strong> a new shared memory segment identified by
                 <span class="param">key</span>. If
                 a segment with that key already exists, the call raises
-                a <tt>ExistentialError</tt>.
+                a <code>ExistentialError</code>.
 
-                <p>When both <tt>IPC_CREX</tt> is specified
+                <p>When both <code>IPC_CREX</code> is specified
                     and the caller has write permission, each byte in the new memory segment will be
                     initialized to the value of <span class="param">init_character</span>.
                 </p>
@@ -552,43 +546,43 @@ with sysv_ipc.Semaphore(name) as sem:
 
             <li>When creating an new segment,
                 many (most? all?) operating systems insist on a <span class="param">size</span>
-                &gt; <tt>0</tt>.
+                &gt; <code>0</code>.
                 In addition, some round the size
                 up to the next multiple of PAGE_SIZE.
             </li>
         </ul>
 
         <p>This module supplies a default
-            <span class="param">size</span> of <tt>PAGE_SIZE</tt> when
-            <tt>IPC_CREX</tt> is specified and <tt>0</tt> otherwise.
+            <span class="param">size</span> of <code>PAGE_SIZE</code> when
+            <code>IPC_CREX</code> is specified and <code>0</code> otherwise.
         </p>
     </dd>
 
     <dt id="attach">attach([address = None, [flags = 0]])</dt>
     <dd>
         Attaches this process to the shared memory. The memory must be attached
-        before calling <tt>.read()</tt> or <tt>.write()</tt>. Note that the
+        before calling <code>.read()</code> or <code>.write()</code>. Note that the
         constructor automatically attaches the memory
         so you won't need to call this method unless you explicitly detach it
         and then want to use it again.
 
         <p>The address parameter allows one to specify (as a Python long) a memory
             address at which to attach the segment. Passing None (the default)
-            is equivalent to passing NULL to <tt>shmat()</tt>. See that
+            is equivalent to passing NULL to <code>shmat()</code>. See that
             function's man page for details.
         </p>
 
         <p>The flags are mostly only relevant if one specifies a specific address.
-            One exception is the flag <tt>SHM_RDONLY</tt> which, surprisingly,
+            One exception is the flag <code>SHM_RDONLY</code> which, surprisingly,
             attaches the segment read-only.
         </p>
 
-        <p>Note that on some (and perhaps all) platforms, each call to <tt>.attach()</tt>
+        <p>Note that on some (and perhaps all) platforms, each call to <code>.attach()</code>
             increments the system's "attached" count. Thus, if each call to
-            <tt>.attach()</tt> isn't paired with a call to <tt>.detach()</tt>,
+            <code>.attach()</code> isn't paired with a call to <code>.detach()</code>,
             the system's "attached" count for the shared memory segment will not
             go to zero when the process exits. As a result, the shared memory
-            segment may not disappear even when its creator calls <tt>.remove()</tt>
+            segment may not disappear even when its creator calls <code>.remove()</code>
             and exits.
         </p>
     </dd>
@@ -599,7 +593,7 @@ with sysv_ipc.Semaphore(name) as sem:
     <dt>read([byte_count = 0, [offset = 0]])</dt>
     <dd>Reads up to <span class="param">byte_count</span> bytes from the
         shared memory segment starting at <span class="param">offset</span>
-        and returns them as a bytes object (which is the same as a <tt>str</tt>
+        and returns them as a bytes object (which is the same as a <code>str</code>
         under Python 2).
 
         <p>If <span class="param">byte_count</span> is zero (the default) the
@@ -615,16 +609,16 @@ with sysv_ipc.Semaphore(name) as sem:
     </dd>
 
     <dt>write(some_bytes, [offset = 0])</dt>
-    <dd>Writes bytes (i.e. <tt>str</tt> in Python 2) to the shared memory,
+    <dd>Writes bytes (i.e. <code>str</code> in Python 2) to the shared memory,
         starting at <span class="param">offset</span>. Passing a Unicode object
         may work, but doing so is unsupported and may be explicitly deprecated
         in a future version.
 
         <p>If the offset + data would write outside of the segment,
-        	this function raises <tt>ValueError</tt>.
+        	this function raises <code>ValueError</code>.
         </p>
 
-        <p>The bytes may contain embedded NULL bytes ('\0').
+        <p>The bytes may contain embedded NULL bytes (<code>'\0'</code>).
     </dd>
 
     <dt>remove()</dt>
@@ -681,7 +675,7 @@ with sysv_ipc.Semaphore(name) as sem:
 
         <p>Tip: the following Python code will display
         the mode in octal:<br>
-        <tt>print int(str(my_mem.mode), 8)</tt>
+        <code>print int(str(my_mem.mode), 8)</code>
         </p>
     </dd>
 
@@ -703,10 +697,10 @@ with sysv_ipc.Semaphore(name) as sem:
     <dt>MessageQueue(key, [flags = 0, [mode = 0600, [max_message_size = 2048]]])</dt>
     <dd>Creates a new message queue or opens an existing one.
 
-        <p><span class="param">key</span> must be <tt>None</tt>,
-            <tt>IPC_PRIVATE</tt> or
-        an integer &gt; <tt>0</tt> and &le; <tt>KEY_MAX</tt>. If the key
-        is <tt>None</tt>, the module chooses a random unused key.
+        <p><span class="param">key</span> must be <code>None</code>,
+            <code>IPC_PRIVATE</code> or
+        an integer &gt; <code>0</code> and &le; <code>KEY_MAX</code>. If the key
+        is <code>None</code>, the module chooses a random unused key.
         </p>
 
         <p>The <span class="param">flags</span> specify whether you want to create a
@@ -715,25 +709,25 @@ with sysv_ipc.Semaphore(name) as sem:
 
         <ul>
             <li>With <span class="param">flags</span> set to the
-                <strong>default</strong> of <tt>0</tt>, the module attempts
+                <strong>default</strong> of <code>0</code>, the module attempts
                 to <strong>open an existing</strong> message queue identified by
                 <span class="param">key</span> and raises
-                a <tt>ExistentialError</tt> if it doesn't exist.
+                a <code>ExistentialError</code> if it doesn't exist.
             </li>
 
-            <li>With <span class="param">flags</span> set to <strong><tt>IPC_CREAT</tt></strong>, the module
+            <li>With <span class="param">flags</span> set to <strong><code>IPC_CREAT</code></strong>, the module
                 <strong>opens</strong> the message queue identified
                 by <span class="param">key</span> <strong>or
                 creates</strong> a new one if no such queue exists.
             </li>
 
             <li>With <span class="param">flags</span> set to
-                <strong><tt>IPC_CREX</tt></strong> (<tt>IPC_CREAT | IPC_EXCL</tt>),
+                <strong><code>IPC_CREX</code></strong> (<code>IPC_CREAT | IPC_EXCL</code>),
                 the module
                 <strong>creates</strong> a new message queue identified by
                 <span class="param">key</span>. If
                 a queue with that key already exists, the call raises
-                a <tt>ExistentialError</tt>.
+                a <code>ExistentialError</code>.
             </li>
         </ul>
 
@@ -747,8 +741,8 @@ with sysv_ipc.Semaphore(name) as sem:
     <dd>Puts a message on the queue.
 
         <p>The <span class="param">message</span> should be a bytes object
-        (a.k.a. <tt>str</tt> in Python 2) and can contain embedded
-            NULLs (ASCII <tt>0x00</tt>). Passing a Unicode object
+        (a.k.a. <code>str</code> in Python 2) and can contain embedded
+            NULLs (ASCII <code>0x00</code>). Passing a Unicode object
         may work, but doing so is unsupported and may be explicitly deprecated
         in a future version.
         </p>
@@ -756,26 +750,26 @@ with sysv_ipc.Semaphore(name) as sem:
         <p>The <span class="param">block</span> flag specifies whether or
             not the call should wait if the message can't be sent (if, for
             example, the queue is full). When <span class="param">block</span>
-            is <tt>False</tt>, the call will raise a <tt>BusyError</tt> if
+            is <code>False</code>, the call will raise a <code>BusyError</code> if
             the message can't be sent immediately.
         </p>
 
         <p>The <span class="param">type</span> is
             associated with the message and is relevant when calling
-            <tt>receive()</tt>. It must be &gt; 0.
+            <code>receive()</code>. It must be &gt; 0.
         </p>
     </dd>
 
     <dt>receive([block = True, [type = 0]])</dt>
     <dd>
         Receives a message from the queue, returning a tuple of
-        <tt>(message, type)</tt>. The message is a bytes object
-        (a.k.a. <tt>str</tt> in Python 2).
+        <code>(message, type)</code>. The message is a bytes object
+        (a.k.a. <code>str</code> in Python 2).
 
         <p>The <span class="param">block</span> flag specifies whether or
             not the call should wait if there's no messages of the
             specified type to retrieve. When <span class="param">block</span>
-            is <tt>False</tt>, the call will raise a <tt>BusyError</tt> if
+            is <code>False</code>, the call will raise a <code>BusyError</code> if
             a message can't be received immediately.
         </p>
 
@@ -784,14 +778,14 @@ with sysv_ipc.Semaphore(name) as sem:
         </p>
 
         <ul>
-            <li>When <span class="param">type</span> <tt>== 0</tt>, the call
+            <li>When <span class="param">type</span> <code>== 0</code>, the call
                 returns the first message on the queue regardless of its
                 type.
             </li>
-            <li>When <span class="param">type</span> <tt>&gt; 0</tt>, the call
+            <li>When <span class="param">type</span> <code>&gt; 0</code>, the call
                 returns the first message of that type.
             </li>
-            <li>When <span class="param">type</span> <tt>&lt; 0</tt>, the call
+            <li>When <span class="param">type</span> <code>&lt; 0</code>, the call
                 returns the first message of the lowest type that is ≤ the
                 absolute value of <span class="param">type</span>.
             </li>
@@ -847,7 +841,7 @@ with sysv_ipc.Semaphore(name) as sem:
 
         <p>Tip: the following Python code will display
         the mode in octal:<br>
-        <tt>print int(str(my_mem.mode), 8)</tt>
+        <code>print int(str(my_mem.mode), 8)</code>
         </p>
     </dd>
 
@@ -863,61 +857,61 @@ with sysv_ipc.Semaphore(name) as sem:
 <h4 id='samples'>Sample Code</h4>
 
 <p>This module comes with four sets of demonstration code in the
-directory <tt>demos</tt>.</p>
+directory <code>demos</code>.</p>
 
 <ul>
-	<li><tt>sem_and_shm</tt> demonstrates using semaphores and
+	<li><code>sem_and_shm</code> demonstrates using semaphores and
 		shared memory to share data between processes.
 	</li>
-	<li><tt>message_queues</tt> demonstrates using message queues
+	<li><code>message_queues</code> demonstrates using message queues
 		 to share data between processes.
 	</li>
-	<li><tt>buffer_protocol</tt> demonstrates creating <tt>bytearray</tt> and
-		<tt>memoryview</tt> objects atop shared memory.
+	<li><code>buffer_protocol</code> demonstrates creating <code>bytearray</code> and
+		<code>memoryview</code> objects atop shared memory.
 	</li>
-	<li><tt>semaphore_context_manager</tt> demonstrates using
-		semaphore instances in a context manager (<tt>with</tt> statement).
+	<li><code>semaphore_context_manager</code> demonstrates using
+		semaphore instances in a context manager (<code>with</code> statement).
 	</li>
 
 </ul>
 
 
 
-<h4 id="ftok_weakness">The Weakness of <tt>ftok()</tt></h4>
+<h4 id="ftok_weakness">The Weakness of <code>ftok()</code></h4>
 
 <p>
-Most System V IPC sample code recommends <tt>ftok()</tt> for generating an
+Most System V IPC sample code recommends <code>ftok()</code> for generating an
 integer key that's more-or-less random.
 It does not, however, guarantee that the key it generates is unused. If
-<tt>ftok()</tt> gives your application a key that some other application is
+<code>ftok()</code> gives your application a key that some other application is
 already using,
 your app is in trouble unless it has a reliable second mechanism for generating
-a key. And if that's the case, why not just abandon <tt>ftok()</tt> and use the
+a key. And if that's the case, why not just abandon <code>ftok()</code> and use the
 second mechanism exclusively?
 </p>
 
-<p>This is the weakness of <tt>ftok()</tt> -- it isn't guaranteed to give you
+<p>This is the weakness of <code>ftok()</code> -- it isn't guaranteed to give you
 what you want. The <a href="http://www.unix.com/man-page/FreeBSD/3/ftok/">BSD
-man page for <tt>ftok</tt></a> says it is "quite possible for the routine to
+man page for <code>ftok</code></a> says it is "quite possible for the routine to
 return duplicate keys". The term "quite possible" isn't quantified, but suppose
 it means one-tenth of one percent. Who wants to have 1-in-1000 odds of a
 catastrophic failure in their program, or even 1-in-10000?
 </p>
 
-<p>This module obviates the need for <tt>ftok()</tt> by generating random
-keys for you. If your application can't use <tt>sysv_ipc</tt>'s automatically
+<p>This module obviates the need for <code>ftok()</code> by generating random
+keys for you. If your application can't use <code>sysv_ipc</code>'s automatically
 generated keys because it needs to know the key in advance, hardcoding a
 random number like 123456 in your app might be no worse than using
-<tt>ftok()</tt> and has the advantage of not hiding its limitations.
+<code>ftok()</code> and has the advantage of not hiding its limitations.
 </p>
 
-<p>This module provides <tt>ftok()</tt> in case you want to experiment with it.
-However, to emphasize its weakness, this version of <tt>ftok()</tt> raises a
+<p>This module provides <code>ftok()</code> in case you want to experiment with it.
+However, to emphasize its weakness, this version of <code>ftok()</code> raises a
 warning with every call unless you explicitly pass a flag to silence it.
 </p>
 
-<p>This package also provides <tt>ftok_experiment.py</tt> so that you can observe
-how often <tt>ftok()</tt> generates duplicate keys on your system.
+<p>This package also provides <code>ftok_experiment.py</code> so that you can observe
+how often <code>ftok()</code> generates duplicate keys on your system.
 </p>
 
 
@@ -926,7 +920,7 @@ how often <tt>ftok()</tt> generates duplicate keys on your system.
 <p>When a System V sempahore is created at the C API level, the OS is not required
 to initialize the semaphore's value. (This per
 <a href="http://www.opengroup.org/onlinepubs/009695399/functions/semget.html">the
-SUSv3 standard for <tt>semget()</tt></a>.)
+SUSv3 standard for <code>semget()</code></a>.)
 Some (most? all?) operating systems initialize it to zero, but this behavior
 is non-standard and therefore can't be relied upon.
 </p>
@@ -934,11 +928,11 @@ is non-standard and therefore can't be relied upon.
 <p>If sempahore creation happens in an predictable, orderly fashion, this isn't a
 problem. But a
 race condition arises when multiple processes vie to create/open the same semaphore. The
-problem lies in the fact that when an application calls <tt>semget()</tt> with only
-the <tt>IPC_CREAT</tt> flag, the caller can't tell whether or not he has
+problem lies in the fact that when an application calls <code>semget()</code> with only
+the <code>IPC_CREAT</code> flag, the caller can't tell whether or not he has
 created a new semaphore or opened an existing one.
 <strong>This makes it
-difficult to create reliable code without using <tt>IPC_EXCL</tt>.</strong>
+difficult to create reliable code without using <code>IPC_EXCL</code>.</strong>
 W. Richard Stevens' <span class='book_title'>Unix Network Programming Volume 2</span>
 calls this "a fatal flaw in the design of System V semaphores" (p 284).
 </p>
@@ -950,24 +944,24 @@ Consider the following sequence of events at the startup of P1 and P2 &ndash;
 </p>
 
 <ol>
-    <li>P1 calls <tt>semget(IPC_CREAT)</tt> to create the semaphore S.</li>
-    <li>P2 calls <tt>semget(IPC_CREAT)</tt> to open S.</li>
+    <li>P1 calls <code>semget(IPC_CREAT)</code> to create the semaphore S.</li>
+    <li>P2 calls <code>semget(IPC_CREAT)</code> to open S.</li>
     <li>P1 initializes the semaphore's value to 1.</li>
-    <li>P1 calls <tt>acquire()</tt>, decrementing the value to 0.</li>
+    <li>P1 calls <code>acquire()</code>, decrementing the value to 0.</li>
     <li>P2, assuming S is a newly-created semaphore that needs to be initialized,
         incorrectly sets the semaphore's value to 1.</li>
-    <li>P2 calls <tt>acquire()</tt>, decrementing the value to 0. Both processes
+    <li>P2 calls <code>acquire()</code>, decrementing the value to 0. Both processes
         now think they own the lock.</li>
 </ol>
 
 <p>W. Richard Stevens' solution for this race condition is to check the value of
-<tt>sem_otime</tt> (an element in the <tt>semid_ds</tt> struct that's
-populated on the call to <tt>semctl(IPC_STAT)</tt> and which is exposed to
+<code>sem_otime</code> (an element in the <code>semid_ds</code> struct that's
+populated on the call to <code>semctl(IPC_STAT)</code> and which is exposed to
 Python by this module) which
 is initialized to zero when the semaphore is created and otherwise holds
 the time of the last
-call to <tt>semop()</tt> (which is called by <tt>P()</tt>/<tt>acquire()</tt>,
-<tt>V()</tt>/<tt>release()</tt>, and <tt>Z()</tt>).
+call to <code>semop()</code> (which is called by <code>P()</code>/<code>acquire()</code>,
+<code>V()</code>/<code>release()</code>, and <code>Z()</code>).
 </p>
 
 <p>In Python, each process would run something like this:
@@ -990,7 +984,7 @@ else:
 <h4 id="mem_init">Shared Memory Initialization</h4>
 
 <p>With shared memory,
-using the <tt>IPC_CREAT</tt> flag without <tt>IPC_EXCL</tt>
+using the <code>IPC_CREAT</code> flag without <code>IPC_EXCL</code>
 is problematic <em>unless you know the size of the segment
 you're potentially opening</em>.
 </p>
@@ -999,14 +993,14 @@ you're potentially opening</em>.
 many (most? all?) operating systems demand a non-zero size. However,
 when opening an existing segment, zero is the only guaranteed safe value
 (again, assuming one doesn't know the size of the segment in advance).
-Since <tt>IPC_CREAT</tt>
+Since <code>IPC_CREAT</code>
 can open or create a segment, there's no safe value for the size under
 this circumstance.
 </p>
 
 <p>As a (sort of) side note, the
 <a href="http://www.opengroup.org/onlinepubs/009695399/functions/shmget.html">SUSv3
-specification for <tt>shmget()</tt></a> says only that the size of a new
+specification for <code>shmget()</code></a> says only that the size of a new
 segment must not be less than "the system-imposed minimum". I
 gather that at one time, some systems set the minimum at zero despite the
 fact that it doesn't make much sense to create a zero-length shared memory
@@ -1039,7 +1033,7 @@ a recompile.
 <p><strong>This module can't figure out what the limits are</strong>, so
 it can't cushion them or even report them to you.
 On some systems the limits are expressed in header files, on others
-they're available through kernel interfaces (like FreeBSD's <tt>sysctl</tt>).
+they're available through kernel interfaces (like FreeBSD's <code>sysctl</code>).
 Under macOS and to some extent OpenSolaris I can't figure out where they're
 defined and what I report here is the result of experimentation and educated
 guesses formed by Googling.
@@ -1047,7 +1041,7 @@ guesses formed by Googling.
 
 <p>The good news is that this module will still behave as advertised no
 matter what these limits are. Nevertheless you might be surprised when a
-call to <tt>.send()</tt> get stuck because a queue is full even though you've
+call to <code>.send()</code> get stuck because a queue is full even though you've
 only put 2048 bytes of messages in it.
 </p>
 
@@ -1060,7 +1054,7 @@ improved over the 2009 numbers I describe below.
 
 <p>Under <strong>OpenSolaris 2008.05</strong> each queue's maximum size defaults
 to 64k. A privileged process (e.g. root) can change this through the
-<tt>max_size</tt> attribute of a <tt>sysv_ipc.MessageQueue</tt> object.
+<code>max_size</code> attribute of a <code>sysv_ipc.MessageQueue</code> object.
 I was able to increase it to 16M and successfully sent sixteen 1M messages to
 the queue.
 </p>
@@ -1072,7 +1066,7 @@ increase this to 16M, but only for a privileged process.
 
 <p>Under <strong>FreeBSD 7</strong> and I think NetBSD and OpenBSD, each
 queue's maximum size defaults to 2048 bytes. Furthermore, one can (as root)
-set <tt>max_size</tt> to something larger and FreeBSD doesn't complain, but
+set <code>max_size</code> to something larger and FreeBSD doesn't complain, but
 it also ignores the change.
 </p>
 
@@ -1087,8 +1081,8 @@ message queue limits</a>.
 
 <p>If you want
 to search for these limits on your operating system, the key constants are
-<tt>MSGSEG</tt>, <tt>MSGSSZ</tt>, <tt>MSGTQL</tt>, <tt>MSGMNB</tt>,
-<tt>MSGMNI</tt> and <tt>MSGMAX</tt>. Under BSD, <tt>sysctl kern.ipc</tt>
+<code>MSGSEG</code>, <code>MSGSSZ</code>, <code>MSGTQL</code>, <code>MSGMNB</code>,
+<code>MSGMNI</code> and <code>MSGMAX</code>. Under BSD, <code>sysctl kern.ipc</code>
 should tell you what you need to know and may allow you to change these
 parameters.
 </p>
@@ -1104,17 +1098,17 @@ won't go away when your process ends unless you explicitly remove it.
 
 <p>In short, remember to clean up after yourself.</p>
 
-<h4>Consult Your Local <tt>man</tt> Pages</h4>
+<h4>Consult Your Local <code>man</code> Pages</h4>
 
 <p>The sysv_ipc module is just a wrapper around your system's API. If your
-system's implementation has quirks, the <tt>man</tt> pages for <tt>semget, semctl, semop
-shmget, shmat, shmdt</tt> and <tt>shmctl</tt> will probably cover them.
+system's implementation has quirks, the <code>man</code> pages for <code>semget, semctl, semop
+shmget, shmat, shmdt</code> and <code>shmctl</code> will probably cover them.
 </p>
 
 <h4>Interesting Tools</h4>
 
 <p>Many systems (although not some older versions of OS X) come
-with <tt>ipcs</tt> and <tt>ipcrm</tt>.
+with <code>ipcs</code> and <code>ipcrm</code>.
 The former shows existing shared memory, semaphores and message queues on your system and
 the latter allows you to remove them.
 </p>
@@ -1152,7 +1146,7 @@ difficulty, user interest and so forth.
 <ul>
     <li>Update this documentation with a list of platforms that support semtimedop().</li>
 
-    <li>Find a way to make <tt>SEMAPHORE_VALUE_MAX</tt> more accurate.</li>
+    <li>Find a way to make <code>SEMAPHORE_VALUE_MAX</code> more accurate.</li>
 </ul>
 
 <p>I don't plan on adding support for semaphore sets.</p>
diff --git a/VERSION b/VERSION
index 5d08204..9084fa2 100644
--- a/VERSION
+++ b/VERSION
@@ -1,3 +1 @@
-1.0.0
-
-
+1.1.0
diff --git a/common.c b/common.c
index 73f7e20..5c125fa 100644
--- a/common.c
+++ b/common.c
@@ -34,18 +34,6 @@ get_random_key(void) {
     return (key_t)key;
 }
 
-#if PY_MAJOR_VERSION < 3
-PyObject *
-py_int_or_long_from_ulong(unsigned long value) {
-    // Python ints are guaranteed to accept up to LONG_MAX. Anything
-    // larger needs to be a Python long.
-    if (value > LONG_MAX)
-        return PyLong_FromUnsignedLong(value);
-    else
-        return PyInt_FromLong(value);
-}
-#endif
-
 
 int
 convert_key_param(PyObject *py_key, void *converted_key) {
@@ -62,12 +50,6 @@ convert_key_param(PyObject *py_key, void *converted_key) {
         rc = 1;
         ((NoneableKey *)converted_key)->is_none = 1;
     }
-#if PY_MAJOR_VERSION < 3
-    else if (PyInt_Check(py_key)) {
-        rc = 1;
-        key = PyInt_AsLong(py_key);
-    }
-#endif
     else if (PyLong_Check(py_key)) {
         rc = 1;
         key = PyLong_AsLong(py_key);
diff --git a/common.h b/common.h
index 3403199..4e3e433 100644
--- a/common.h
+++ b/common.h
@@ -103,83 +103,48 @@ Freebsd - l_int (int)
 // I assume it is a long; see comment above.
 // Some functions return (key_t)-1, so I guess this has to be a signed type.
 // ref: http://www.opengroup.org/austin/interps/doc.tpl?gdid=6226
-#if PY_MAJOR_VERSION > 2
-    #define KEY_T_TO_PY(key)   PyLong_FromLong(key)
-#else
-    #define KEY_T_TO_PY(key)   PyInt_FromLong(key)
-#endif
+#define KEY_T_TO_PY(key)   PyLong_FromLong(key)
+
 // SUSv3 guarantees a uid_t to be an integer type. Some functions return
 // (uid_t)-1, so I guess this has to be a signed type.
 // ref: http://www.opengroup.org/onlinepubs/009695399/basedefs/sys/types.h.html
 // ref: http://www.opengroup.org/onlinepubs/9699919799/functions/chown.html
-#if PY_MAJOR_VERSION > 2
-    #define UID_T_TO_PY(uid)   PyLong_FromLong(uid)
-#else
-    #define UID_T_TO_PY(uid)   PyInt_FromLong(uid)
-#endif
+#define UID_T_TO_PY(uid)   PyLong_FromLong(uid)
 
 // SUSv3 guarantees a gid_t to be an integer type. Some functions return
 // (gid_t)-1, so I guess this has to be a signed type.
 // ref: http://www.opengroup.org/onlinepubs/009695399/basedefs/sys/types.h.html
-#if PY_MAJOR_VERSION > 2
-    #define GID_T_TO_PY(gid)   PyLong_FromLong(gid)
-#else
-    #define GID_T_TO_PY(gid)   PyInt_FromLong(gid)
-#endif
+#define GID_T_TO_PY(gid)   PyLong_FromLong(gid)
 
 // I'm not sure what guarantees SUSv3 makes about a mode_t, but param 3 of
 // shmget() is an int that contains flags in addition to the mode, so
 // mode must be able to fit into an int.
 // ref: http://www.opengroup.org/onlinepubs/009695399/functions/shmget.html
-#if PY_MAJOR_VERSION > 2
-    #define MODE_T_TO_PY(mode)   PyLong_FromLong(mode)
-#else
-    #define MODE_T_TO_PY(mode)   PyInt_FromLong(mode)
-#endif
+#define MODE_T_TO_PY(mode)   PyLong_FromLong(mode)
 
 // I'm not sure what guarantees SUSv3 makes about a time_t, but the times
 // I deal with here are all guaranteed to be after 1 Jan 1970 which means
 // they'll always be positive numbers. A ulong sounds appropriate to me,
 // and Python agrees in posixmodule.c.
-#if PY_MAJOR_VERSION > 2
-    #define TIME_T_TO_PY(time)   PyLong_FromUnsignedLong(time)
-#else
-    #define TIME_T_TO_PY(time)   py_int_or_long_from_ulong(time)
-#endif
+#define TIME_T_TO_PY(time)   PyLong_FromUnsignedLong(time)
 
 // C89 guarantees a size_t to be unsigned and fit into a ulong or smaller.
-#if PY_MAJOR_VERSION > 2
-    #define SIZE_T_TO_PY(size)   PyLong_FromUnsignedLong(size)
-#else
-    #define SIZE_T_TO_PY(size)   py_int_or_long_from_ulong(size)
-#endif
+#define SIZE_T_TO_PY(size)   PyLong_FromUnsignedLong(size)
 
 // SUSv3 guarantees a pid_t to be a signed integer type. Some functions
 // return (pid_t)-1 so I guess this has to be signed.
 // ref: http://www.opengroup.org/onlinepubs/000095399/basedefs/sys/types.h.html#tag_13_67
-#if PY_MAJOR_VERSION > 2
-    #define PID_T_TO_PY(pid)   PyLong_FromLong(pid)
-#else
-    #define PID_T_TO_PY(pid)   PyInt_FromLong(pid)
-#endif
+#define PID_T_TO_PY(pid)   PyLong_FromLong(pid)
 
 // The SUS guarantees a msglen_t to be an unsigned integer type.
 // Ditto: msgqnum_t.
 // ref: http://www.opengroup.org/onlinepubs/000095399/basedefs/sys/msg.h.html
-#if PY_MAJOR_VERSION > 2
-    #define MSGLEN_T_TO_PY(msglen)     PyLong_FromUnsignedLong(msglen)
-    #define MSGQNUM_T_TO_PY(msgqnum)   PyLong_FromUnsignedLong(msgqnum)
-#else
-    #define MSGLEN_T_TO_PY(msglen)     py_int_or_long_from_ulong(msglen)
-    #define MSGQNUM_T_TO_PY(msgqnum)   py_int_or_long_from_ulong(msgqnum)
-#endif
+#define MSGLEN_T_TO_PY(msglen)     PyLong_FromUnsignedLong(msglen)
+#define MSGQNUM_T_TO_PY(msgqnum)   PyLong_FromUnsignedLong(msgqnum)
 
 /* Utility functions */
 key_t get_random_key(void);
 int convert_key_param(PyObject *, void *);
-#if PY_MAJOR_VERSION < 3
-PyObject *py_int_or_long_from_ulong(unsigned long);
-#endif
 
 /* Custom Exceptions/Errors */
 extern PyObject *pBaseException;
diff --git a/debian/changelog b/debian/changelog
index c9ebe4b..41a48a1 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+python-sysv-ipc (1.0.0+git20210117.3.d8b463a-1) UNRELEASED; urgency=low
+
+  * New upstream snapshot.
+  * New upstream snapshot.
+  * New upstream snapshot.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Fri, 30 Dec 2022 16:59:39 -0000
+
 python-sysv-ipc (1.0.0-2) unstable; urgency=medium
 
   * Uploading to unstable.
diff --git a/demos/buffer_protocol/demo.py b/demos/buffer_protocol/demo.py
index fb42b16..04e1950 100644
--- a/demos/buffer_protocol/demo.py
+++ b/demos/buffer_protocol/demo.py
@@ -1,19 +1,11 @@
-import sys
-
 import sysv_ipc
 
-IS_PY3 = (sys.version_info[0] == 3)
-to_chr = lambda c: chr(c) if IS_PY3 else c  # noqa E731  (silence flake8)
-to_ord = lambda c: ord(c) if IS_PY3 else c  # noqa E731  (silence flake8)
-
 # Create a shared memory segment and write the (English) alphabet to the shared memory.
 mem = sysv_ipc.SharedMemory(None, sysv_ipc.IPC_CREX, size=sysv_ipc.PAGE_SIZE)
 
 ASCII_A = 0x61
 alphabet = ''.join([chr(ASCII_A + i) for i in range(26)])
-if IS_PY3:
-    alphabet = bytes(alphabet, 'ASCII')
-
+alphabet = bytes(alphabet, 'ASCII')
 mem.write(alphabet)
 
 # Create a bytearray from the SharedMemory.
@@ -39,16 +31,16 @@ mv = memoryview(mem)
 # strides = (1, ), and is read/write.
 
 # This shows that you can take slices of a memoryview
-assert([to_chr(c) for c in mv[3:6]] == ['d', 'e', 'f'])
+assert([chr(c) for c in mv[3:6]] == ['d', 'e', 'f'])
 
 # This shows that you can write to the memoryview.
-mv[4] = to_ord('x')
+mv[4] = ord('x')
 
-assert([to_chr(c) for c in mv[3:6]] == ['d', 'x', 'f'])
+assert([chr(c) for c in mv[3:6]] == ['d', 'x', 'f'])
 
 # Changes to the underlying segment are reflected in the memoryview
 mem.write(b'xxx')
-assert([to_chr(c) for c in mv[:6]] == ['x', 'x', 'x', 'd', 'x', 'f'])
+assert([chr(c) for c in mv[:6]] == ['x', 'x', 'x', 'd', 'x', 'f'])
 
 mem.detach()
 mem.remove()
diff --git a/demos/message_queues/conclusion.py b/demos/message_queues/conclusion.py
index 3682c7e..7cbc476 100644
--- a/demos/message_queues/conclusion.py
+++ b/demos/message_queues/conclusion.py
@@ -1,5 +1,4 @@
 # Python modules
-import sys
 import hashlib
 
 # 3rd party modules
@@ -8,8 +7,6 @@ import sysv_ipc
 # Utils for this demo
 import utils
 
-PY_MAJOR_VERSION = sys.version_info[0]
-
 params = utils.read_params()
 
 # Mrs. Premise has already created the message queue. I just need a handle to it.
@@ -33,8 +30,7 @@ for i in range(0, params["ITERATIONS"]):
         utils.say("Received %s" % s)
 
     if what_i_sent:
-        if PY_MAJOR_VERSION > 2:
-            what_i_sent = what_i_sent.encode()
+        what_i_sent = what_i_sent.encode()
         try:
             assert(s == hashlib.md5(what_i_sent).hexdigest())
         except AssertionError:
diff --git a/demos/message_queues/premise.py b/demos/message_queues/premise.py
index 409e1d6..2e137ac 100644
--- a/demos/message_queues/premise.py
+++ b/demos/message_queues/premise.py
@@ -1,6 +1,5 @@
 # Python modules
 import time
-import sys
 import hashlib
 
 # 3rd party modules
@@ -9,8 +8,6 @@ import sysv_ipc
 # Utils for this demo
 import utils
 
-PY_MAJOR_VERSION = sys.version_info[0]
-
 utils.say("Oooo 'ello, I'm Mrs. Premise!")
 
 params = utils.read_params()
@@ -42,8 +39,7 @@ for i in range(0, params["ITERATIONS"]):
 
     # What I read must be the md5 of what I wrote or something's
     # gone wrong.
-    if PY_MAJOR_VERSION > 2:
-        what_i_sent = what_i_sent.encode()
+    what_i_sent = what_i_sent.encode()
     try:
         assert(s == hashlib.md5(what_i_sent).hexdigest())
     except AssertionError:
diff --git a/demos/sem_and_shm/conclusion.py b/demos/sem_and_shm/conclusion.py
index 4b4fbb6..6a6002f 100755
--- a/demos/sem_and_shm/conclusion.py
+++ b/demos/sem_and_shm/conclusion.py
@@ -1,5 +1,4 @@
 # Python modules
-import sys
 import hashlib
 
 # 3rd party modules
@@ -8,8 +7,6 @@ import sysv_ipc
 # Utils for this demo
 import utils
 
-PY_MAJOR_VERSION = sys.version_info[0]
-
 utils.say("Oooo 'ello, I'm Mrs. Conclusion!")
 
 params = utils.read_params()
@@ -43,15 +40,13 @@ for i in range(0, params["ITERATIONS"]):
         s = utils.read_from_memory(memory)
 
     if what_i_wrote:
-        if PY_MAJOR_VERSION > 2:
-            what_i_wrote = what_i_wrote.encode()
+        what_i_wrote = what_i_wrote.encode()
         try:
             assert(s == hashlib.md5(what_i_wrote).hexdigest())
         except AssertionError:
             raise AssertionError("Shared memory corruption after %d iterations." % i)
 
-    if PY_MAJOR_VERSION > 2:
-        s = s.encode()
+    s = s.encode()
     what_i_wrote = hashlib.md5(s).hexdigest()
 
     utils.write_to_memory(memory, what_i_wrote)
diff --git a/demos/sem_and_shm/params.txt b/demos/sem_and_shm/params.txt
old mode 100644
new mode 100755
index eff48bd..ce0e2a9
--- a/demos/sem_and_shm/params.txt
+++ b/demos/sem_and_shm/params.txt
@@ -2,14 +2,14 @@
 
 # ITERATIONS is the number of times they'll talk to one another.
 # LIVE_DANGEROUSLY is a Boolean (0 or 1); if set to 1 the programs
-#    won't use the semaphore to coordinate access to the shared 
+#    won't use the semaphore to coordinate access to the shared
 #    memory. Corruption will likely result.
 # KEY is the key to be used for the semaphore and shared memory.
 # PERMISSIONS are in octal (note the leading 0).
 # SHM_SIZE is the size of the shared memory segment in bytes.
 
 ITERATIONS=1000
-LIVE_DANGEROUSLY=0
+LIVE_DANGEROUSLY=1
 KEY=42
 PERMISSIONS=0600
 SHM_SIZE=4096
diff --git a/demos/sem_and_shm/premise.py b/demos/sem_and_shm/premise.py
index a486457..94e7cd3 100755
--- a/demos/sem_and_shm/premise.py
+++ b/demos/sem_and_shm/premise.py
@@ -1,6 +1,5 @@
 # Python modules
 import time
-import sys
 import hashlib
 
 # 3rd party modules
@@ -9,8 +8,6 @@ import sysv_ipc
 # Utils for this demo
 import utils
 
-PY_MAJOR_VERSION = sys.version_info[0]
-
 utils.say("Oooo 'ello, I'm Mrs. Premise!")
 
 params = utils.read_params()
@@ -63,8 +60,7 @@ for i in range(0, params["ITERATIONS"]):
         s = utils.read_from_memory(memory)
 
     # What I read must be the md5 of what I wrote or something's gone wrong.
-    if PY_MAJOR_VERSION > 2:
-        what_i_wrote = what_i_wrote.encode()
+    what_i_wrote = what_i_wrote.encode()
 
     try:
         assert(s == hashlib.md5(what_i_wrote).hexdigest())
@@ -72,8 +68,7 @@ for i in range(0, params["ITERATIONS"]):
         raise AssertionError("Shared memory corruption after %d iterations." % i)
 
     # MD5 the reply and write back to Mrs. Conclusion.
-    if PY_MAJOR_VERSION > 2:
-        s = s.encode()
+    s = s.encode()
     what_i_wrote = hashlib.md5(s).hexdigest()
     utils.write_to_memory(memory, what_i_wrote)
 
diff --git a/demos/sem_and_shm/utils.py b/demos/sem_and_shm/utils.py
index 112677c..855dce9 100644
--- a/demos/sem_and_shm/utils.py
+++ b/demos/sem_and_shm/utils.py
@@ -1,12 +1,7 @@
 import time
 import sys
 
-PY_MAJOR_VERSION = sys.version_info[0]
-
-if PY_MAJOR_VERSION > 2:
-    NULL_CHAR = 0
-else:
-    NULL_CHAR = '\0'
+NULL_CHAR = '\0'
 
 
 def say(s):
@@ -19,17 +14,15 @@ def say(s):
 
 def write_to_memory(memory, s):
     say("writing %s " % s)
-    s += '\0'
-    if PY_MAJOR_VERSION > 2:
-        s = s.encode()
+    s += NULL_CHAR
+    s = s.encode()
     memory.write(s)
 
 
 def read_from_memory(memory):
     s = memory.read()
-    if PY_MAJOR_VERSION > 2:
-        s = s.decode()
-    i = s.find('\0')
+    s = s.decode()
+    i = s.find(NULL_CHAR)
     if i != -1:
         s = s[:i]
     say("read %s" % s)
diff --git a/extras/explore_max_semaphore_value.py b/extras/explore_max_semaphore_value.py
index 9d1641b..e057e74 100644
--- a/extras/explore_max_semaphore_value.py
+++ b/extras/explore_max_semaphore_value.py
@@ -11,4 +11,4 @@ for i in range(1, 100000):
 
     print('{:05}: value is {}'.format(i, sem.value))
 
-sem.remove()
\ No newline at end of file
+sem.remove()
diff --git a/extras/ftok_experiment.py b/extras/ftok_experiment.py
index 39d2b75..9139b63 100644
--- a/extras/ftok_experiment.py
+++ b/extras/ftok_experiment.py
@@ -3,17 +3,11 @@ import sys
 import os
 import sysv_ipc
 
-# Python 2's raw_input() has become input() in Python 3.
-if sys.version_info[0] == 3:
-    input_method = input
-else:
-    input_method = raw_input  # noqa - tell flake8 to chill
-
 if len(sys.argv) == 2:
     start_path = sys.argv[1]
 else:
     msg = "Start path? [Default = your home directory] "
-    start_path = input_method(msg)
+    start_path = input(msg)
     if not start_path:
         start_path = "~"
 
@@ -23,6 +17,7 @@ start_path = os.path.abspath(start_path)
 
 # For every filename in the tree, generate a key and associate the filename
 # with that key via a dictionary.
+print(f"Scanning {start_path}...")
 d = {}
 nfilenames = 0
 for path, dirnames, filenames in os.walk(start_path):
@@ -48,4 +43,4 @@ for key in d:
         ndups += len(d[key])
         print(key, d[key])
 
-print("Out of {0} unique filenames, I found {1} duplicate keys.".format(nfilenames, ndups))
+print(f"Out of {nfilenames} unique filenames, I found {ndups} duplicate keys.")
diff --git a/extras/memory_leak_tests.py b/extras/memory_leak_tests.py
index 73a8c05..3ffda4c 100644
--- a/extras/memory_leak_tests.py
+++ b/extras/memory_leak_tests.py
@@ -18,12 +18,10 @@ SKIP_MESSAGE_QUEUE_TESTS = False
 # TEST_COUNT = 10
 TEST_COUNT = 1024 * 100
 
-PY_MAJOR_VERSION = sys.version_info[0]
-
 # ps output looks like this:
 #   RSZ      VSZ
 #   944    75964
-ps_output_regex = re.compile("""
+ps_output_regex = re.compile(r"""
     ^
     \s*   # whitespace before first heading
     \S*   # first heading (e.g. RSZ)
@@ -77,8 +75,7 @@ def get_memory_usage():
     #   RSZ      VSZ
     #   944    75964
 
-    if PY_MAJOR_VERSION > 2:
-        s = s.decode(sys.getfilesystemencoding())
+    s = s.decode(sys.getfilesystemencoding())
 
     m = ps_output_regex.match(s)
 
@@ -467,26 +464,25 @@ else:
 
     print_mem_after()
 
-    if PY_MAJOR_VERSION > 2:
-        print("Running memory read/write test with bytes...")
-        print_mem_before()
+    print("Running memory read/write test with bytes...")
+    print_mem_before()
 
-        mem = sysv_ipc.SharedMemory(42, sysv_ipc.IPC_CREX, size=sysv_ipc.PAGE_SIZE)
-        alphabet = "abcdefghijklmnopqrstuvwxyz".encode()
+    mem = sysv_ipc.SharedMemory(42, sysv_ipc.IPC_CREX, size=sysv_ipc.PAGE_SIZE)
+    alphabet = "abcdefghijklmnopqrstuvwxyz".encode()
 
-        s = alphabet
-        length = len(s)
-        for i in range(1, TEST_COUNT):
-            # length = random.randint(1, sysv_ipc.PAGE_SIZE)
-            # s = ''.join( [ random.choice(alphabet) for j in range(1, length + 1) ] )
-            mem.write(s)
-            assert(s == mem.read(length))
+    s = alphabet
+    length = len(s)
+    for i in range(1, TEST_COUNT):
+        # length = random.randint(1, sysv_ipc.PAGE_SIZE)
+        # s = ''.join( [ random.choice(alphabet) for j in range(1, length + 1) ] )
+        mem.write(s)
+        assert(s == mem.read(length))
 
-        mem.detach()
+    mem.detach()
 
-        mem.remove()
+    mem.remove()
 
-        print_mem_after()
+    print_mem_after()
 
     print("Running memory key read test...")
     print_mem_before()
@@ -795,20 +791,19 @@ else:
 
     print_mem_after()
 
-    if PY_MAJOR_VERSION > 2:
-        print("Running message queue send/receive test with bytes...")
-        print_mem_before()
+    print("Running message queue send/receive test with bytes...")
+    print_mem_before()
 
-        mq = sysv_ipc.MessageQueue(42, sysv_ipc.IPC_CREX)
+    mq = sysv_ipc.MessageQueue(42, sysv_ipc.IPC_CREX)
 
-        for i in range(1, TEST_COUNT):
-            s = random_string(15).encode()
-            mq.send(s)
-            assert(s == mq.receive()[0])
+    for i in range(1, TEST_COUNT):
+        s = random_string(15).encode()
+        mq.send(s)
+        assert(s == mq.receive()[0])
 
-        mq.remove()
+    mq.remove()
 
-        print_mem_after()
+    print_mem_after()
 
     print("Running message queue key read test...")
     print_mem_before()
diff --git a/history.html b/history.html
index b548b98..9714b18 100644
--- a/history.html
+++ b/history.html
@@ -1,16 +1,16 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<!DOCTYPE html>
 
-<html>
+<html lang='en'>
 
 <head>
 	<meta charset="utf-8">
 	<meta name="author" content="Philip Semanchuk">
-	<meta name="copyright" content="All contents &copy; 2018 Philip Semanchuk">
+	<meta name="copyright" content="All contents &copy; 2021 Philip Semanchuk">
 	<meta name="keywords" content="python sysv system v ipc semaphore shared memory message queue">
 
 	<title>The sysv_ipc Module for SysV IPC Under Python -- Version History</title>
 
-	<style type="text/css">
+	<style>
 		li {
 			margin-top: .67em;
 			margin-bottom: .67em;
@@ -26,13 +26,30 @@
 <a href="http://semanchuk.com/philip/sysv_ipc/">sysv_ipc module</a>.</p>
 
 <ul>
-	<li id="v1_0_0"><strong><span id="current">Current</span> &ndash; 1.0.0 (2 Feb 2018) &ndash;</strong>
+	<li id="v1_1_0"><strong><span id="current">Current</span> &ndash; 1.1.0 (17 Jan 2021) &ndash;</strong>
+		<ul>
+			<li>Drop support for Python 2, also for Python 3.4 and 3.5.</li>
+			<li><strong>Behavior change:</strong> <code>ftok()</code> now raises <code>OSError</code> if it fails so the particulars of the failure are visible to the caller. Thanks to James Williams for the suggestion. (Previously the function just returned -1 if it failed.)
+			</li>
+		</ul>
+	</li>
+
+	<li id="v1_0_1">1.0.1 (29 Nov 2019) &ndash;
+		<ul>
+			<li>Fix
+				<a href="https://github.com/osvenskan/sysv_ipc/issues/7">a memory leak bug in <code>SharedMemory.write()</code> that only occurred under Python 3</a>. Thanks to Andy for reporting!
+			</li>
+			<li>This will be the last version to support Python 2 which is pining for the fjords.</li>
+		</ul>
+	</li>
+
+	<li>1.0.0 (2 Feb 2018) &ndash;
 		<p>I'm bumping the version number to 1.0.0 because I now consider this feature complete.
 			That doesn't mean I won't add features, but I don't feel like I need to.
 		</p>
 
 		<ul>
-			<li>Added support for Python <tt>memoryview</tt> and <tt>bytearray</tt> objects to be created from a <tt>SharedMemory</tt> instance.
+			<li>Added support for Python <code>memoryview</code> and <code>bytearray</code> objects to be created from a <code>SharedMemory</code> instance.
 			</li>
 			<li>Added support for building wheels.</li>
 			<li>Moved source code hosting to git and <a href='https://github.com/osvenskan/sysv_ipc'>GitHub</a>.</li>
@@ -45,36 +62,36 @@
 		<p>Version 0.7.0 is finally out now that this package is 7 years old...</p>
 
 		<ul>
-			<li>Dropped compatibility with Python < 2.7.</li>
+			<li>Dropped compatibility with Python &lt; 2.7.</li>
 
 			<li>Added unit tests with almost complete coverage.</li>
 
-			<li>Fixed a doc bug for <tt>SharedMemory.write()</tt>. On an
+			<li>Fixed a doc bug for <code>SharedMemory.write()</code>. On an
 				attempt to write outside of the segment, the code raises
-				<tt>ValueError</tt>. The documentation had incorrectly stated that
-				<tt>write()</tt> would silently
+				<code>ValueError</code>. The documentation had incorrectly stated that
+				<code>write()</code> would silently
 				discard the data that overflowed the segment.
 			</li>
 
-			<li>Fixed a code bug in <tt>SharedMemory.write()</tt> that
-				incorrectly interpreted offsets &gt; <tt>LONG_MAX</tt>.
+			<li>Fixed a code bug in <code>SharedMemory.write()</code> that
+				incorrectly interpreted offsets &gt; <code>LONG_MAX</code>.
 			</li>
 
-			<li>Fixed a code bug in <tt>SharedMemory.write()</tt> that
-				didn’t always generate a <tt>ValueError</tt> for negative
+			<li>Fixed a code bug in <code>SharedMemory.write()</code> that
+				didn’t always generate a <code>ValueError</code> for negative
 				offsets.
 			</li>
 
-			<li>Fixed a doc/code bug where <tt>SharedMemory.attach()</tt> was
+			<li>Fixed a doc/code bug where <code>SharedMemory.attach()</code> was
 				documented to accept keyword arguments but did not. Now it does.
 			</li>
 
 			<li>Fixed some header bugs that prevented the code from using
-				<tt>PY_SSIZE_T_CLEAN</tt>. Thanks to Tilman Koschnick for
+				<code>PY_SSIZE_T_CLEAN</code>. Thanks to Tilman Koschnick for
 				bringing this to my attention.
 			</li>
 
-			<li>Added doc to note that this module exposes <tt>SHM_RDONLY</tt>
+			<li>Added doc to note that this module exposes <code>SHM_RDONLY</code>
 				as a module-level constant.
 			</li>
 		</ul>
@@ -82,9 +99,9 @@
 
 	<li id="v0_6_8">0.6.8 (12 Sept 2014) &ndash;
 		<ul>
-			<li>Fixed a bug in <tt>prober.py</tt> where prober would fail
+			<li>Fixed a bug in <code>prober.py</code> where prober would fail
 				on systems where Python's include file was not named
-				<tt>pyconfig.h</tt>. (RHEL 6 is one such system.) Thanks to
+				<code>pyconfig.h</code>. (RHEL 6 is one such system.) Thanks to
 				Joshua Harlow for the bug report and patch.
 			</li>
 
@@ -134,10 +151,10 @@
 
 	<li id="v0_6_4">0.6.4 (19 Dec 2012) &ndash;
 		<ul>
-			<li>Added a module-level <tt>attach()</tt> method based on a
+			<li>Added a module-level <code>attach()</code> method based on a
 				suggestion by Vladimír Včelák.
 			</li>
-			<li>Added the <tt>ftok()</tt> method along with dire warnings about
+			<li>Added the <code>ftok()</code> method along with dire warnings about
 				its use.
 			</li>
 		</ul>
@@ -145,7 +162,7 @@
 
 	<li id="v0_6_3">0.6.3 (3 Oct 2010) &ndash;
 		<ul>
-			<li>Fixed a segfault in <tt>write()</tt> that occurred any time
+			<li>Fixed a segfault in <code>write()</code> that occurred any time
 				an offset was passed. This was introduced in v0.6.0.
 				<i>Tack</i> to Johan Bernhardtson for the bug report.
 			</li>
@@ -164,7 +181,7 @@
 		<ul>
 			<li>Fixed a typo introduced in the previous version that caused
 				unpredictable behavior (usually a segfault) if a block flag
-				was passed to <tt>MessageQueue.send()</tt>. <i>Obrigado</i>
+				was passed to <code>MessageQueue.send()</code>. <i>Obrigado</i>
 				to Álvaro Justen and Ronald Kaiser for the bug report and
 				patch.
 			</li>
@@ -187,8 +204,8 @@
 				(Daniel) for the bug report and patch.
 			</li>
 			<li>Fixed a bug where the module could have generated invalid
-				keys on systems that typedef <tt>key_t</tt> as a
-				<tt>short</tt>. I don't think such a system exists, though.
+				keys on systems that typedef <code>key_t</code> as a
+				<code>short</code>. I don't think such a system exists, though.
 			</li>
 			<li>Fixed a LICENSE file typo.</li>
 			<li>Added an RSS feed to this page.</li>
@@ -198,14 +215,14 @@
 	<li id="v0_5_1">0.5.1 (1 Dec 2009) &ndash;
 		<p>No code changes in this version.</p>
 		<ul>
-			<li>Fixed the comment in <tt>sysv_ipc_module.c</tt> that
+			<li>Fixed the comment in <code>sysv_ipc_module.c</code> that
 				still referred to the GPL license.
 			</li>
-			<li>Added the attributes <tt>__version</tt>, <tt>__author__</tt>,
-				<tt>__license__</tt> and <tt>__copyright__</tt>.
+			<li>Added the attributes <code>__version</code>, <code>__author__</code>,
+				<code>__license__</code> and <code>__copyright__</code>.
 			</li>
-			<li>Removed <tt>file()</tt> from <tt>setup.py</tt> in favor
-				of <tt>open()</tt>.
+			<li>Removed <code>file()</code> from <code>setup.py</code> in favor
+				of <code>open()</code>.
 			</li>
 		</ul>
 	</li>
@@ -230,17 +247,17 @@
 			<li>Changed status to beta.</li>
 			<li>Added automatic generation of keys.</li>
 			<li>Added a message queue demo.</li>
-			<li>Added <tt>str()</tt> and <tt>repr()</tt> support to all
+			<li>Added <code>str()</code> and <code>repr()</code> support to all
 				objects.</li>
-			<li>Fixed a bug in <tt>SharedMemory.write()</tt> that could cause
+			<li>Fixed a bug in <code>SharedMemory.write()</code> that could cause
 				a spurious error of "Attempt to write past end of memory
 				segment". This bug only occurred on platforms using
-				different sizes for <tt>long</tt> and <tt>int</tt>, or
-				<tt>long</tt> and <tt>py_size_t</tt>
+				different sizes for <code>long</code> and <code>int</code>, or
+				<code>long</code> and <code>py_size_t</code>
 				(depending on Python version). <em>Tack</em> to Jesper
 				for debug help.
 			</li>
-			<li>Plugged a memory leak in <tt>MessageQueue.receive()</tt>.</li>
+			<li>Plugged a memory leak in <code>MessageQueue.receive()</code>.</li>
 			<li>Added a VERSION attribute to the module.</li>
 		</ul>
 	</li>
@@ -248,14 +265,14 @@
 	<li id="v0_4">0.4 (28 Jan 2009) &ndash;
 		<ul>
 			<li>Added message queues.</li>
-			<li>Fixed a bug where the <tt>key</tt> attribute of SharedMemory objects
+			<li>Fixed a bug where the <code>key</code> attribute of SharedMemory objects
 				returned garbage.
 			</li>
 			<li>Fixed a bug where keys &gt; INT_MAX would get truncated on
 				platforms where longs are bigger than ints.
 			</li>
 			<li>Provided decent inline documentation for object attributes
-				(available via the <tt>help()</tt> command).
+				(available via the <code>help()</code> command).
 			</li>
 			<li>Rearranged the code which was suffering growing pains.</li>
 		</ul>
@@ -267,15 +284,15 @@
 			semaphore timeouts, and many small fixes.
 		</p>
 
-		<p>A semaphore's <tt>.block</tt> flag now consistently trumps the
-			timeout. When <tt>False</tt>, the timeout is irrelevant -- calls
+		<p>A semaphore's <code>.block</code> flag now consistently trumps the
+			timeout. When <code>False</code>, the timeout is irrelevant -- calls
 			will never block. In prior versions, the flag was ignored
 			when the timeout was non-zero.
 		</p>
 
-		<p>Also, on platforms (such as OS X) where <tt>semtimedop()</tt> is
-			not supported, all timeouts are now treated as <tt>None</tt>.
-			In other words, when <tt>.block</tt> is True, all calls
+		<p>Also, on platforms (such as OS X) where <code>semtimedop()</code> is
+			not supported, all timeouts are now treated as <code>None</code>.
+			In other words, when <code>.block</code> is True, all calls
 			wait as long as necessary.
 		</p>
 
@@ -295,36 +312,36 @@
 			<li>Got rid of OS X compile warnings.</li>
 			<li>Fixed many instances where I was making potentially lossy
 				conversions between Python values and Unix-specific
-				types like <tt>key_t</tt>, <tt>pid_t</tt>, etc.
+				types like <code>key_t</code>, <code>pid_t</code>, etc.
 			</li>
-			<li>Fixed a bug in the SharedMemory <tt>attach()</tt> function
+			<li>Fixed a bug in the SharedMemory <code>attach()</code> function
 				 that would set an error string but not return an error
-				 value if the passed address was not <tt>None</tt> or a long.
+				 value if the passed address was not <code>None</code> or a long.
 			</li>
 			<li>Simplified the code a little.</li>
-			<li>Restricted the semaphore <tt>acquire()</tt> and
-				<tt>release()</tt> delta to be between SHORT_MIN and
+			<li>Restricted the semaphore <code>acquire()</code> and
+				<code>release()</code> delta to be between SHORT_MIN and
 				SHORT_MAX since
 				<a href="http://opengroup.org/onlinepubs/007908775/xsh/semop.html">it
 					is a short in the SUSv2 spec</a>.
 			</li>
-			<li>Fixed a bug where calling <tt>acquire()</tt> or
-				<tt>release()</tt> with a delta of <tt>0</tt> would call
-				<tt>.Z()</tt> instead.
+			<li>Fixed a bug where calling <code>acquire()</code> or
+				<code>release()</code> with a delta of <code>0</code> would call
+				<code>.Z()</code> instead.
 			</li>
-			<li>Disallowed byte counts ≤ <tt>0</tt> in
-				<tt>SharedMemory.read()</tt>
+			<li>Disallowed byte counts ≤ <code>0</code> in
+				<code>SharedMemory.read()</code>
 			</li>
-			<li>Fixed a bug in the <tt>SharedMemory</tt> init code that
+			<li>Fixed a bug in the <code>SharedMemory</code> init code that
 				could, under vanishingly rare circumstances, return failure
 				without setting the error code.
 			</li>
-			<li>Removed some dead code relating to the <tt>.seq</tt> member
-				of the <tt>sem_perm</tt> and <tt>shm_perm</tt> structs.
+			<li>Removed some dead code relating to the <code>.seq</code> member
+				of the <code>sem_perm</code> and <code>shm_perm</code> structs.
 			</li>
-			<li>Stopped accessing the non-standard <tt>key</tt>
-				(a.k.a. <tt>_key</tt> and <tt>__key</tt>) element of the
-				<tt>ipc_perm</tt> struct.
+			<li>Stopped accessing the non-standard <code>key</code>
+				(a.k.a. <code>_key</code> and <code>__key</code>) element of the
+				<code>ipc_perm</code> struct.
 			</li>
 			<li>Added code to ensure the module doesn't try to create
 				a string that's larger than Python permits when reading
@@ -336,15 +353,15 @@
 	<li>0.2.1 (3 Jan 2009) &ndash;
 		<ul>
 			<li>Fixed a bug that prevented the module-specific
-				errors (<tt>ExistentialError</tt>, etc.) from
+				errors (<code>ExistentialError</code>, etc.) from
 				being visible in the module.
 			</li>
 			<li>Fixed a bug that re-initialized shared memory with
-				the init character when only <tt>IPC_CREAT</tt> was specified
+				the init character when only <code>IPC_CREAT</code> was specified
 				and an existing segment was opened.
 			</li>
 			<li>Fixed a bug that always defaulted the size of a shared
-				memory segment to <tt>PAGE_SIZE</tt>. Updated code and
+				memory segment to <code>PAGE_SIZE</code>. Updated code and
 				documentation to use intelligent defaults. <em>Tack</em> to
 				Jesper for the bug report.
 			</li>
@@ -359,7 +376,7 @@
 		Lots of small fixes.
 
 		<ul>
-			<li>Fixed a bug where calling <tt>shm.remove()</tt> on shared
+			<li>Fixed a bug where calling <code>shm.remove()</code> on shared
 			   memory that was already removed would cause a SystemError.
 			   (I wasn't setting the Python error before returning.)
 			</li>
@@ -372,14 +389,14 @@
 				of a new, read-only semaphore to fail.
 			</li>
 
-			<li>Added the constant <tt>IPC_CREX</tt>.</li>
+			<li>Added the constant <code>IPC_CREX</code>.</li>
 
-			<li>Renamed (sorry) <tt>MAX_KEY</tt> to <tt>KEY_MAX</tt> and
-				<tt>MAX_SEMAPHORE_VALUE</tt> to <tt>SEMAPHORE_VALUE_MAX</tt>
+			<li>Renamed (sorry) <code>MAX_KEY</code> to <code>KEY_MAX</code> and
+				<code>MAX_SEMAPHORE_VALUE</code> to <code>SEMAPHORE_VALUE_MAX</code>
 				to be consistent with the C naming convention in limits.h.
 			</li>
 
-			<li>Hardcoded <tt>SEMAPHORE_VALUE_MAX</tt> to 32767 until I can
+			<li>Hardcoded <code>SEMAPHORE_VALUE_MAX</code> to 32767 until I can
 				find a reliable way to determine it at install time.
 			</li>
 
diff --git a/keys.c b/keys.c
deleted file mode 100644
index 12f44dd..0000000
--- a/keys.c
+++ /dev/null
@@ -1,19 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h> 
-#include <limits.h>
-
-int main() { 
-    int i;
-    int key;
-    
-
-    for (i = 0; i < 10000000; i++) {
-        // ref: http://www.c-faq.com/lib/randrange.html
-        key = ((int)((double)rand() / ((double)RAND_MAX + 1) * INT_MAX)) + 1;
-        printf("%d\n", key);
-    }
-    
- 
-    
-    return 1;
-}
\ No newline at end of file
diff --git a/memory.c b/memory.c
index f6242d5..0d05506 100644
--- a/memory.c
+++ b/memory.c
@@ -9,20 +9,12 @@
 /******************    Internal use only     **********************/
 PyObject *
 shm_str(SharedMemory *self) {
-#if PY_MAJOR_VERSION > 2
-    return PyUnicode_FromFormat("Key=%ld, id=%d", (long)self->key, self->id);
-#else
-    return PyString_FromFormat("Key=%ld, id=%d", (long)self->key, self->id);
-#endif
+	return PyUnicode_FromFormat("Key=%ld, id=%d", (long)self->key, self->id);
 }
 
 PyObject *
 shm_repr(SharedMemory *self) {
-#if PY_MAJOR_VERSION > 2
     return PyUnicode_FromFormat("sysv_ipc.SharedMemory(%ld)", (long)self->key);
-#else
-    return PyString_FromFormat("sysv_ipc.SharedMemory(%ld)", (long)self->key);
-#endif
 }
 
 PyObject *
@@ -159,11 +151,7 @@ shm_get_value(int shared_memory_id, enum GET_SET_IDENTIFIERS field) {
         case SVIFP_SHM_NUMBER_ATTACHED:
             // shm_nattch is unsigned
             // ref: http://www.opengroup.org/onlinepubs/007908799/xsh/sysshm.h.html
-#if PY_MAJOR_VERSION > 2
             py_value = PyLong_FromUnsignedLong(shm_info.shm_nattch);
-#else
-            py_value = py_int_or_long_from_ulong(shm_info.shm_nattch);
-#endif
         break;
 
         case SVIFP_IPC_PERM_UID:
@@ -286,11 +274,7 @@ shm_get_buffer(SharedMemory *self, Py_buffer *view, int flags)
         return -1;
     }
     else {
-#if PY_MAJOR_VERSION > 2
     	size = PyLong_AsSsize_t(py_size);
-#else
-    	size = PyInt_AsSsize_t(py_size);
-#endif
     	Py_DECREF(py_size);
 	    return PyBuffer_FillInfo(view,
 	    						 (PyObject *)self,
@@ -443,11 +427,7 @@ SharedMemory_init(SharedMemory *self, PyObject *args, PyObject *keywords) {
         if (!py_size)
             goto error_return;
         else {
-#if PY_MAJOR_VERSION > 2
             size = PyLong_AsUnsignedLongMask(py_size);
-#else
-            size = PyInt_AsUnsignedLongMask(py_size);
-#endif
 
             DPRINTF("memsetting address %p to %lu bytes of ASCII 0x%x (%c)\n", \
                     self->address, size, (int)init_character, init_character);
@@ -546,11 +526,7 @@ SharedMemory_read(SharedMemory *self, PyObject *args, PyObject *keywords) {
     }
 
     if ( (py_size = shm_get_value(self->id, SVIFP_SHM_SIZE)) ) {
-#if PY_MAJOR_VERSION > 2
         size = PyLong_AsUnsignedLongMask(py_size);
-#else
-        size = PyInt_AsUnsignedLongMask(py_size);
-#endif
         Py_DECREF(py_size);
     }
     else
@@ -590,12 +566,7 @@ SharedMemory_read(SharedMemory *self, PyObject *args, PyObject *keywords) {
         }
     }
 
-
-#if PY_MAJOR_VERSION > 2
     return PyBytes_FromStringAndSize(self->address + offset, byte_count);
-#else
-    return PyString_FromStringAndSize(self->address + offset, byte_count);
-#endif
 
     error_return:
     return NULL;
@@ -617,44 +588,26 @@ SharedMemory_write(SharedMemory *self, PyObject *args, PyObject *kw) {
     unsigned long size;
     PyObject *py_size;
     char *keyword_list[ ] = {"s", "offset", NULL};
-#if PY_MAJOR_VERSION > 2
     static char args_format[] = "s*|k";
     Py_buffer data;
-#else
-    static char args_format[] = "s#|k";
-    typedef struct {
-        const char *buf;
-        long len;
-    } MyBuffer;
-    MyBuffer data;
-    data.len = 0;
-#endif
-
-    if (self->read_only) {
-        PyErr_SetString(PyExc_OSError, "Write attempt on read-only memory segment");
-        goto error_return;
-    }
 
     if (!PyArg_ParseTupleAndKeywords(args, kw, args_format, keyword_list,
-#if PY_MAJOR_VERSION > 2
                           &data,
-#else
-                          &(data.buf), &(data.len),
-#endif
                           &offset))
         goto error_return;
 
+    if (self->read_only) {
+        PyErr_SetString(PyExc_OSError, "Write attempt on read-only memory segment");
+        goto error_return;
+    }
+
     if (self->address == NULL) {
         PyErr_SetString(pNotAttachedException, "Write attempt on unattached memory segment");
         goto error_return;
     }
 
     if ( (py_size = shm_get_value(self->id, SVIFP_SHM_SIZE)) ) {
-#if PY_MAJOR_VERSION > 2
         size = PyLong_AsUnsignedLongMask(py_size);
-#else
-        size = PyInt_AsUnsignedLongMask(py_size);
-#endif
         Py_DECREF(py_size);
     }
     else
@@ -673,9 +626,12 @@ SharedMemory_write(SharedMemory *self, PyObject *args, PyObject *kw) {
 
     memcpy((self->address + offset), data.buf, data.len);
 
+    PyBuffer_Release(&data);
+
     Py_RETURN_NONE;
 
     error_return:
+    PyBuffer_Release(&data);
     return NULL;
 }
 
@@ -762,21 +718,13 @@ int
 shm_set_uid(SharedMemory *self, PyObject *py_value) {
     union ipc_perm_value new_value;
 
-#if PY_MAJOR_VERSION > 2
     if (!PyLong_Check(py_value))
-#else
-    if (!PyInt_Check(py_value))
-#endif
     {
         PyErr_SetString(PyExc_TypeError, "Attribute 'uid' must be an integer");
         goto error_return;
     }
 
-#if PY_MAJOR_VERSION > 2
     new_value.uid = PyLong_AsLong(py_value);
-#else
-    new_value.uid = PyInt_AsLong(py_value);
-#endif
 
     if (((uid_t)-1 == new_value.uid) && PyErr_Occurred()) {
         // no idea what could have gone wrong -- punt it up to the caller
@@ -799,21 +747,13 @@ int
 shm_set_gid(SharedMemory *self, PyObject *py_value) {
     union ipc_perm_value new_value;
 
-#if PY_MAJOR_VERSION > 2
     if (!PyLong_Check(py_value))
-#else
-    if (!PyInt_Check(py_value))
-#endif
     {
         PyErr_Format(PyExc_TypeError, "attribute 'gid' must be an integer");
         goto error_return;
     }
 
-#if PY_MAJOR_VERSION > 2
     new_value.gid = PyLong_AsLong(py_value);
-#else
-    new_value.gid = PyInt_AsLong(py_value);
-#endif
 
     if (((gid_t)-1 == new_value.gid) && PyErr_Occurred()) {
         // no idea what could have gone wrong -- punt it up to the caller
@@ -830,21 +770,13 @@ int
 shm_set_mode(SharedMemory *self, PyObject *py_value) {
     union ipc_perm_value new_value;
 
-#if PY_MAJOR_VERSION > 2
     if (!PyLong_Check(py_value))
-#else
-    if (!PyInt_Check(py_value))
-#endif
     {
         PyErr_Format(PyExc_TypeError, "attribute 'mode' must be an integer");
         goto error_return;
     }
 
-#if PY_MAJOR_VERSION > 2
     new_value.mode = PyLong_AsLong(py_value);
-#else
-    new_value.mode = PyInt_AsLong(py_value);
-#endif
 
     if (((mode_t)-1 == new_value.mode) && PyErr_Occurred()) {
         // no idea what could have gone wrong -- punt it up to the caller
diff --git a/mq.c b/mq.c
index 4ad4d37..da50c2a 100644
--- a/mq.c
+++ b/mq.c
@@ -8,21 +8,13 @@
 
 PyObject *
 mq_str(MessageQueue *self) {
-#if PY_MAJOR_VERSION > 2
     return PyUnicode_FromFormat("Key=%ld, id=%d", (long)self->key, self->id);
-#else
-    return PyString_FromFormat("Key=%ld, id=%d", (long)self->key, self->id);
-#endif
 }
 
 
 PyObject *
 mq_repr(MessageQueue *self) {
-#if PY_MAJOR_VERSION > 2
     return PyUnicode_FromFormat("sysv_ipc.MessageQueue(%ld)", (long)self->key);
-#else
-    return PyString_FromFormat("sysv_ipc.MessageQueue(%ld)", (long)self->key);
-#endif
 }
 
 
@@ -119,12 +111,7 @@ int
 set_a_value(int id, enum GET_SET_IDENTIFIERS field, PyObject *py_value) {
     struct msqid_ds mq_info;
 
-#if PY_MAJOR_VERSION > 2
-    if (!PyLong_Check(py_value))
-#else
-    if (!PyInt_Check(py_value))
-#endif
-    {
+    if (!PyLong_Check(py_value)) {
         PyErr_Format(PyExc_TypeError, "The attribute must be an integer");
         goto error_return;
     }
@@ -156,37 +143,21 @@ set_a_value(int id, enum GET_SET_IDENTIFIERS field, PyObject *py_value) {
 
     switch (field) {
         case SVIFP_IPC_PERM_UID:
-#if PY_MAJOR_VERSION > 2
             mq_info.msg_perm.uid = PyLong_AsLong(py_value);
-#else
-            mq_info.msg_perm.uid = PyInt_AsLong(py_value);
-#endif
         break;
 
         case SVIFP_IPC_PERM_GID:
-#if PY_MAJOR_VERSION > 2
             mq_info.msg_perm.gid = PyLong_AsLong(py_value);
-#else
-            mq_info.msg_perm.gid = PyInt_AsLong(py_value);
-#endif
         break;
 
         case SVIFP_IPC_PERM_MODE:
-#if PY_MAJOR_VERSION > 2
             mq_info.msg_perm.mode = PyLong_AsLong(py_value);
-#else
-            mq_info.msg_perm.mode = PyInt_AsLong(py_value);
-#endif
         break;
 
         case SVIFP_MQ_QUEUE_BYTES_MAX:
             // A msglen_t is unsigned.
             // ref: http://www.opengroup.org/onlinepubs/000095399/basedefs/sys/msg.h.html
-#if PY_MAJOR_VERSION > 2
             mq_info.msg_qbytes = PyLong_AsUnsignedLongMask(py_value);
-#else
-            mq_info.msg_qbytes = PyInt_AsUnsignedLongMask(py_value);
-#endif
         break;
 
         default:
@@ -466,18 +437,8 @@ MessageQueue_send(MessageQueue *self, PyObject *args, PyObject *keywords) {
        py_ssize_t. Therefore I *must* initialize it to 0 so that whatever
        Python doesn't write to is zeroed out.
    */
-#if PY_MAJOR_VERSION > 2
     static char args_format[] = "s*|Oi";
     Py_buffer user_msg;
-#else
-    static char args_format[] = "s#|Oi";
-    typedef struct {
-        char *buf;
-        long len;
-    } MyBuffer;
-    MyBuffer user_msg;
-    user_msg.len = 0;
-#endif
     PyObject *py_block = NULL;
     int flags = 0;
     int type = 1;
@@ -487,12 +448,7 @@ MessageQueue_send(MessageQueue *self, PyObject *args, PyObject *keywords) {
 
     // send(message, [block = True, [type = 1]])
     if (!PyArg_ParseTupleAndKeywords(args, keywords, args_format, keyword_list,
-#if PY_MAJOR_VERSION > 2
-                                     &user_msg,
-#else
-                                     &(user_msg.buf), &(user_msg.len),
-#endif
-                                     &py_block, &type))
+                                     &user_msg, &py_block, &type))
         goto error_return;
 
     if (type <= 0) {
@@ -560,19 +516,12 @@ MessageQueue_send(MessageQueue *self, PyObject *args, PyObject *keywords) {
         goto error_return;
     }
 
-
-#if PY_MAJOR_VERSION > 2
     PyBuffer_Release(&user_msg);
-#endif
-
     free(p_msg);
-
     Py_RETURN_NONE;
 
     error_return:
-#if PY_MAJOR_VERSION > 2
     PyBuffer_Release(&user_msg);
-#endif
     free(p_msg);
     return NULL;
 }
@@ -647,13 +596,8 @@ MessageQueue_receive(MessageQueue *self, PyObject *args, PyObject *keywords) {
     }
 
     py_return_tuple = Py_BuildValue("NN",
-#if PY_MAJOR_VERSION > 2
                                     PyBytes_FromStringAndSize(p_msg->message, rc),
                                     PyLong_FromLong(p_msg->type)
-#else
-                                    PyString_FromStringAndSize(p_msg->message, rc),
-                                    PyInt_FromLong(p_msg->type)
-#endif
                                    );
 
     free(p_msg);
@@ -670,4 +614,3 @@ PyObject *
 MessageQueue_remove(MessageQueue *self) {
     return mq_remove(self->id);
 }
-
diff --git a/post_dist.py b/post_dist.py
deleted file mode 100755
index 9607eb2..0000000
--- a/post_dist.py
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/env python
-
-# Python imports
-import time
-import hashlib
-import shutil
-import os
-
-RSS_TIMESTAMP_FORMAT = "%a, %d %b %Y %H:%M:%S GMT"
-
-VERSION = open("VERSION").read().strip()
-
-# Make a copy of the tarball for posterity
-tarball_name = "sysv_ipc-%s.tar.gz" % VERSION
-shutil.copyfile(os.path.join("dist", tarball_name),
-                os.path.join("releases", tarball_name))
-
-tarball_name = "releases/sysv_ipc-%s.tar.gz" % VERSION
-md5_name = "releases/sysv_ipc-%s.md5.txt" % VERSION
-sha1_name = "releases/sysv_ipc-%s.sha1.txt" % VERSION
-
-# Generate hashes of the tarball
-tarball_content = open(tarball_name, 'rb').read()
-for hash_function_name in ('md5', 'sha1', 'sha256'):
-    hash_function = getattr(hashlib, hash_function_name)
-    hash_value = hash_function(tarball_content).hexdigest()
-
-    hash_filename = "releases/sysv_ipc-{}.{}.txt".format(VERSION, hash_function_name)
-
-    open(hash_filename, "wb").write(hash_value.encode('ascii'))
-    print(hash_function_name + " = " + hash_value)
-
-# Print an RSS item suitable for pasting into rss.xml
-timestamp = time.strftime(RSS_TIMESTAMP_FORMAT, time.gmtime())
-
-print("""
-
-        <item>
-            <guid isPermaLink="false">%s</guid>
-            <title>sysv_ipc %s Released</title>
-            <pubDate>%s</pubDate>
-            <link>http://semanchuk.com/philip/sysv_ipc/</link>
-            <description>Version %s of sysv_ipc has been released.
-            </description>
-        </item>
-
-""" % (VERSION, VERSION, timestamp, VERSION))
-
-print("Don't forget this:\ngit tag rel" + VERSION)
diff --git a/prober.py b/prober.py
index 93cbd23..9bb3f40 100644
--- a/prober.py
+++ b/prober.py
@@ -131,9 +131,10 @@ def probe():
                     "PAGE_SIZE",
                     ]
 
-    version = open("VERSION").read().strip()
+    with open("VERSION") as f:
+        version = f.read().strip()
 
-    d["SYSV_IPC_VERSION"] = '"%s"' % version
+    d["SYSV_IPC_VERSION"] = f'"{version}"'
     d["PAGE_SIZE"] = probe_page_size()
     if sniff_semtimedop():
         d["SEMTIMEDOP_EXISTS"] = ""
diff --git a/semaphore.c b/semaphore.c
index 6e0e9f2..3159f5b 100644
--- a/semaphore.c
+++ b/semaphore.c
@@ -32,6 +32,9 @@ union semun {
     int val;                    /* used for SETVAL only */
     struct semid_ds *buf;       /* for IPC_STAT and IPC_SET */
     unsigned short *array;      /* used for GETALL and SETALL */
+#ifdef __linux__
+	struct seminfo  *__buf;  	/* Buffer for IPC_INFO (Linux-specific) */
+#endif
 };
 #endif
 
@@ -55,12 +58,6 @@ convert_timeout(PyObject *py_timeout, void *converted_timeout) {
         rc = 1;
         simple_timeout = PyFloat_AsDouble(py_timeout);
     }
-#if PY_MAJOR_VERSION < 3
-    else if (PyInt_Check(py_timeout)) {
-        rc = 1;
-        simple_timeout = (double)PyInt_AsLong(py_timeout);
-    }
-#endif
     else if (PyLong_Check(py_timeout)) {
         rc = 1;
         simple_timeout = (double)PyLong_AsLong(py_timeout);
@@ -95,21 +92,13 @@ convert_timeout(PyObject *py_timeout, void *converted_timeout) {
 
 PyObject *
 sem_str(Semaphore *self) {
-#if PY_MAJOR_VERSION > 2
     return PyUnicode_FromFormat("Key=%ld, id=%d", (long)self->key, self->id);
-#else
-    return PyString_FromFormat("Key=%ld, id=%d", (long)self->key, self->id);
-#endif
 }
 
 
 PyObject *
 sem_repr(Semaphore *self) {
-#if PY_MAJOR_VERSION > 2
     return PyUnicode_FromFormat("sysv_ipc.Semaphore(%ld)", (long)self->key);
-#else
-    return PyString_FromFormat("sysv_ipc.Semaphore(%ld)", (long)self->key);
-#endif
 }
 
 
@@ -288,11 +277,7 @@ sem_get_semctl_value(int semaphore_id, int cmd) {
         goto error_return;
     }
 
-#if PY_MAJOR_VERSION > 2
     return PyLong_FromLong(rc);
-#else
-    return PyInt_FromLong(rc);
-#endif
 
     error_return:
     return NULL;
@@ -360,11 +345,7 @@ sem_set_ipc_perm_value(int id, enum GET_SET_IDENTIFIERS field, PyObject *py_valu
 
     arg.buf = &sem_info;
 
-#if PY_MAJOR_VERSION > 2
     if (!PyLong_Check(py_value))
-#else
-    if (!PyInt_Check(py_value))
-#endif
     {
         PyErr_Format(PyExc_TypeError, "The attribute must be an integer");
         goto error_return;
@@ -388,27 +369,15 @@ sem_set_ipc_perm_value(int id, enum GET_SET_IDENTIFIERS field, PyObject *py_valu
     // cast. If the user passes a value that's too big, tough cookies.
     switch (field) {
         case SVIFP_IPC_PERM_UID:
-#if PY_MAJOR_VERSION > 2
             sem_info.sem_perm.uid = (uid_t)PyLong_AsLong(py_value);
-#else
-            sem_info.sem_perm.uid = (uid_t)PyInt_AsLong(py_value);
-#endif
         break;
 
         case SVIFP_IPC_PERM_GID:
-#if PY_MAJOR_VERSION > 2
             sem_info.sem_perm.gid = (gid_t)PyLong_AsLong(py_value);
-#else
-            sem_info.sem_perm.gid = (gid_t)PyInt_AsLong(py_value);
-#endif
         break;
 
         case SVIFP_IPC_PERM_MODE:
-#if PY_MAJOR_VERSION > 2
             sem_info.sem_perm.mode = (mode_t)PyLong_AsLong(py_value);
-#else
-            sem_info.sem_perm.mode = (mode_t)PyInt_AsLong(py_value);
-#endif
         break;
 
         default:
@@ -621,21 +590,13 @@ sem_set_value(Semaphore *self, PyObject *py_value)
     union semun arg;
     long value;
 
-#if PY_MAJOR_VERSION > 2
     if (!PyLong_Check(py_value))
-#else
-    if (!PyInt_Check(py_value))
-#endif
     {
 		PyErr_Format(PyExc_TypeError, "Attribute 'value' must be an integer");
         goto error_return;
     }
 
-#if PY_MAJOR_VERSION > 2
     value = PyLong_AsLong(py_value);
-#else
-    value = PyInt_AsLong(py_value);
-#endif
 
     DPRINTF("C value is %ld\n", value);
 
diff --git a/setup.cfg b/setup.cfg
index 9d58686..8b278d5 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,3 +1,7 @@
 [flake8]
 max-line-length = 100
 
+[egg_info]
+tag_build = 
+tag_date = 0
+
diff --git a/setup.py b/setup.py
index 813fe15..3258275 100644
--- a/setup.py
+++ b/setup.py
@@ -32,7 +32,6 @@ classifiers = ["Development Status :: 5 - Production/Stable",
                "Operating System :: POSIX",
                "Operating System :: Unix",
                "Programming Language :: Python",
-               "Programming Language :: Python :: 2",
                "Programming Language :: Python :: 3",
                "Topic :: Utilities"]
 license = "http://creativecommons.org/licenses/BSD/"
diff --git a/sysv_ipc.egg-info/PKG-INFO b/sysv_ipc.egg-info/PKG-INFO
new file mode 100644
index 0000000..c3a49d4
--- /dev/null
+++ b/sysv_ipc.egg-info/PKG-INFO
@@ -0,0 +1,37 @@
+Metadata-Version: 2.1
+Name: sysv-ipc
+Version: 1.1.0
+Summary: System V IPC primitives (semaphores, shared memory and message queues) for Python
+Home-page: http://semanchuk.com/philip/sysv_ipc/
+Download-URL: http://semanchuk.com/philip/sysv_ipc/sysv_ipc-1.1.0.tar.gz
+Author: Philip Semanchuk
+Author-email: philip@semanchuk.com
+Maintainer: Philip Semanchuk
+License: http://creativecommons.org/licenses/BSD/
+Keywords: sysv ipc inter-process communication semaphore shared memory shm message queue
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Operating System :: POSIX :: BSD :: FreeBSD
+Classifier: Operating System :: POSIX :: Linux
+Classifier: Operating System :: POSIX :: SunOS/Solaris
+Classifier: Operating System :: POSIX
+Classifier: Operating System :: Unix
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Utilities
+License-File: LICENSE
+
+Sysv_ipc gives Python programs access to System V semaphores, shared memory 
+and message queues. Most (all?) Unixes (including OS X) support System V IPC. 
+Windows+Cygwin 1.7 might also work. 
+
+Sample code is included.
+
+sysv_ipc is free software (free as in speech and free as in beer) released
+under a 3-clause BSD license. Complete licensing information is available in 
+the LICENSE file.
+
+You might also be interested in the similar POSIX IPC module at:
+http://semanchuk.com/philip/posix_ipc/
diff --git a/sysv_ipc.egg-info/SOURCES.txt b/sysv_ipc.egg-info/SOURCES.txt
new file mode 100644
index 0000000..7d0b0d0
--- /dev/null
+++ b/sysv_ipc.egg-info/SOURCES.txt
@@ -0,0 +1,62 @@
+INSTALL
+LICENSE
+MANIFEST.in
+README
+ReadMe.html
+VERSION
+common.c
+common.h
+history.html
+memory.c
+memory.h
+mq.c
+mq.h
+prober.py
+semaphore.c
+semaphore.h
+setup.cfg
+setup.py
+sysv_ipc_module.c
+demos/buffer_protocol/ReadMe.md
+demos/buffer_protocol/demo.py
+demos/message_queues/ReadMe.txt
+demos/message_queues/SampleIpcConversation.png
+demos/message_queues/cleanup.py
+demos/message_queues/conclusion.py
+demos/message_queues/params.txt
+demos/message_queues/premise.py
+demos/message_queues/utils.py
+demos/sem_and_shm/ReadMe.txt
+demos/sem_and_shm/SampleIpcConversation.png
+demos/sem_and_shm/cleanup.py
+demos/sem_and_shm/conclusion.c
+demos/sem_and_shm/conclusion.py
+demos/sem_and_shm/make_all.sh
+demos/sem_and_shm/md5.c
+demos/sem_and_shm/md5.h
+demos/sem_and_shm/params.txt
+demos/sem_and_shm/premise.c
+demos/sem_and_shm/premise.py
+demos/sem_and_shm/utils.c
+demos/sem_and_shm/utils.h
+demos/sem_and_shm/utils.py
+demos/semaphore_context_manager/ReadMe.txt
+demos/semaphore_context_manager/child.py
+demos/semaphore_context_manager/parent.py
+extras/explore_max_semaphore_value.py
+extras/ftok_experiment.py
+extras/memory_leak_tests.py
+extras/memory_limit_test.py
+prober/probe_page_size.c
+prober/semtimedop_test.c
+prober/sniff_union_semun_defined.c
+sysv_ipc.egg-info/PKG-INFO
+sysv_ipc.egg-info/SOURCES.txt
+sysv_ipc.egg-info/dependency_links.txt
+sysv_ipc.egg-info/top_level.txt
+tests/__init__.py
+tests/base.py
+tests/test_memory.py
+tests/test_message_queues.py
+tests/test_module.py
+tests/test_semaphores.py
\ No newline at end of file
diff --git a/sysv_ipc.egg-info/dependency_links.txt b/sysv_ipc.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/sysv_ipc.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/sysv_ipc.egg-info/top_level.txt b/sysv_ipc.egg-info/top_level.txt
new file mode 100644
index 0000000..c4bbcda
--- /dev/null
+++ b/sysv_ipc.egg-info/top_level.txt
@@ -0,0 +1 @@
+sysv_ipc
diff --git a/sysv_ipc_module.c b/sysv_ipc_module.c
index e1fc3d9..1cce2cc 100644
--- a/sysv_ipc_module.c
+++ b/sysv_ipc_module.c
@@ -2,7 +2,7 @@
 sysv_ipc - A Python module for accessing System V semaphores, shared memory
             and message queues.
 
-Copyright (c) 2018, Philip Semanchuk
+Copyright (c) 2021, Philip Semanchuk
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -136,6 +136,11 @@ sysv_ipc_ftok(PyObject *self, PyObject *args, PyObject *keywords) {
 
     DPRINTF("path=%s, id=%d, rc=%ld\n", path, id, (long)rc);
 
+    if (rc == (key_t)-1) {
+	    PyErr_SetFromErrno(PyExc_OSError);
+	    goto error_return;
+	}
+
     return Py_BuildValue("i", rc);
 
     error_return:
@@ -506,12 +511,6 @@ https://stackoverflow.com/questions/19223721/definition-of-pybufferprocs-in-pyth
 Fortunately all the extra fields in the Python 2 version of the struct can just be NULL.
 */
 PyBufferProcs SharedMemory_as_buffer = {
-#if PY_MAJOR_VERSION == 2
-    (readbufferproc)NULL,
-    (writebufferproc)NULL,
-    (segcountproc)NULL,
-    (charbufferproc)NULL,
-#endif
     (getbufferproc)shm_get_buffer,
     (releasebufferproc)NULL,
 };
@@ -536,12 +535,7 @@ static PyTypeObject SharedMemoryType = {
     0,                                          // tp_getattro
     0,                                          // tp_setattro
     &SharedMemory_as_buffer,                    // tp_as_buffer
-    // Python 2 needs the extra tp_flags Py_TPFLAGS_HAVE_NEWBUFFER.
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
-#if PY_MAJOR_VERSION == 2
-                                             | Py_TPFLAGS_HAVE_NEWBUFFER
-#endif
-    ,                                           // tp_flags
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,   // tp_flags
     "System V shared memory object",            // tp_doc
     0,                                          // tp_traverse
     0,                                          // tp_clear
@@ -752,7 +746,6 @@ static PyMethodDef module_methods[ ] = {
 };
 
 
-#if PY_MAJOR_VERSION > 2
 static struct PyModuleDef this_module = {
 	PyModuleDef_HEAD_INIT,  // m_base
 	"sysv_ipc",             // m_name
@@ -764,20 +757,10 @@ static struct PyModuleDef this_module = {
 	NULL,                   // m_clear
 	NULL                    // m_free
 };
-#endif
-
-
-/* Module init function */
-#if PY_MAJOR_VERSION > 2
-#define SYSV_IPC_INIT_FUNCTION_NAME PyInit_sysv_ipc
-#else
-#define SYSV_IPC_INIT_FUNCTION_NAME initsysv_ipc
-#endif
-
 
 /* Module init function */
 PyMODINIT_FUNC
-SYSV_IPC_INIT_FUNCTION_NAME(void) {
+PyInit_sysv_ipc(void) {
     PyObject *module;
     PyObject *module_dict;
 
@@ -785,11 +768,7 @@ SYSV_IPC_INIT_FUNCTION_NAME(void) {
     // random keys.
     srand((unsigned int)time(NULL));
 
-#if PY_MAJOR_VERSION > 2
     module = PyModule_Create(&this_module);
-#else
-    module = Py_InitModule3("sysv_ipc", module_methods, "System V IPC module");
-#endif
 
     if (!module)
         goto error_return;
@@ -883,15 +862,9 @@ SYSV_IPC_INIT_FUNCTION_NAME(void) {
     else
         PyDict_SetItemString(module_dict, "NotAttachedError", pNotAttachedException);
 
-#if PY_MAJOR_VERSION > 2
     return module;
-#endif
 
     error_return:
-#if PY_MAJOR_VERSION > 2
     return NULL;
-#else
-    ; // Nothing to do
-#endif
 }
 
diff --git a/tests/base.py b/tests/base.py
index 0fe122d..77644c4 100644
--- a/tests/base.py
+++ b/tests/base.py
@@ -1,16 +1,11 @@
 # Python imports
-# Don't add any from __future__ imports here. This code should execute
-# against standard Python.
 import unittest
 import random
-import sys
 import time
 
 # Project imports
 import sysv_ipc
 
-IS_PY3 = (sys.version_info[0] == 3)
-
 
 def make_key():
     """Generate a random key suitable for an IPC object."""
diff --git a/tests/test_memory.py b/tests/test_memory.py
index f35272d..66d78bd 100644
--- a/tests/test_memory.py
+++ b/tests/test_memory.py
@@ -1,15 +1,10 @@
 # Python imports
-# Don't add any from __future__ imports here. This code should execute
-# against standard Python.
 import unittest
 import time
 import os
 
 # Project imports
-# Hack -- add tests directory to sys.path so Python 3 can find base.py.
-import sys
-sys.path.insert(0, os.path.join(os.getcwd(), 'tests'))  # noqa - tell flake8 to chill
-import base as tests_base
+from .base import Base, make_key, sleep_past_granularity
 import sysv_ipc
 
 # Not tested --
@@ -18,7 +13,7 @@ import sysv_ipc
 # - attempt to write to segment attached with SHM_RDONLY gives a segfault under OS X and Linux.
 
 
-class SharedMemoryTestBase(tests_base.Base):
+class SharedMemoryTestBase(Base):
     """base class for SharedMemory test classes"""
     # SIZE should be something that's not a power of 2 since that's more
     # likely to expose odd behavior.
@@ -35,8 +30,7 @@ class SharedMemoryTestBase(tests_base.Base):
 
     def assertWriteToReadOnlyPropertyFails(self, property_name, value):
         """test that writing to a readonly property raises TypeError"""
-        tests_base.Base.assertWriteToReadOnlyPropertyFails(self, self.mem,
-                                                           property_name, value)
+        Base.assertWriteToReadOnlyPropertyFails(self, self.mem, property_name, value)
 
 
 class TestSharedMemoryCreation(SharedMemoryTestBase):
@@ -61,7 +55,7 @@ class TestSharedMemoryCreation(SharedMemoryTestBase):
     def test_IPC_CREAT_new(self):
         """tests sysv_ipc.IPC_CREAT to create a new SharedMemory without IPC_EXCL"""
         # I can't pass None for the name unless I also pass IPC_EXCL.
-        key = tests_base.make_key()
+        key = make_key()
 
         # Note: this method of finding an unused key is vulnerable to a race
         # condition. It's good enough for test, but don't copy it for use in
@@ -75,7 +69,7 @@ class TestSharedMemoryCreation(SharedMemoryTestBase):
             except sysv_ipc.ExistentialError:
                 key_is_available = True
             else:
-                key = tests_base.make_key()
+                key = make_key()
 
         mem = sysv_ipc.SharedMemory(key, sysv_ipc.IPC_CREAT, size=sysv_ipc.PAGE_SIZE)
 
@@ -337,7 +331,7 @@ class TestSharedMemoryPropertiesAndAttributes(SharedMemoryTestBase):
         """exercise SharedMemory.last_attach_time"""
         self.mem.detach()
         original_last_attach_time = self.mem.last_attach_time
-        tests_base.sleep_past_granularity()
+        sleep_past_granularity()
         # I can't record exactly when this attach() happens, but as long as it is within 5 seconds
         # of the assertion happening, this test will pass.
         self.mem.attach()
@@ -349,7 +343,7 @@ class TestSharedMemoryPropertiesAndAttributes(SharedMemoryTestBase):
     def test_property_last_detach_time(self):
         """exercise SharedMemory.last_detach_time"""
         original_last_detach_time = self.mem.last_detach_time
-        tests_base.sleep_past_granularity()
+        sleep_past_granularity()
         # I can't record exactly when this detach() happens, but as long as it is within 5 seconds
         # of the assertion happening, this test will pass.
         self.mem.detach()
@@ -361,7 +355,7 @@ class TestSharedMemoryPropertiesAndAttributes(SharedMemoryTestBase):
     def test_property_last_change_time(self):
         """exercise SharedMemory.last_change_time"""
         original_last_change_time = self.mem.last_change_time
-        tests_base.sleep_past_granularity()
+        sleep_past_granularity()
         # I can't record exactly when this last_change_time is set, but as long as it is within
         # 5 seconds of the assertion happening, this test will pass.
         # The statement below might seem like a no-op, but setting the UID to any value triggers
@@ -416,8 +410,7 @@ class BufferProtocolTest(unittest.TestCase):
         ASCII_A = 0x61
         self.alphabet = ''.join([chr(ASCII_A + i) for i in range(26)])
 
-        if tests_base.IS_PY3:
-            self.alphabet = bytes(self.alphabet, 'ASCII')
+        self.alphabet = bytes(self.alphabet, 'ASCII')
 
         self.mem.write(self.alphabet)
 
@@ -460,21 +453,16 @@ class BufferProtocolTest(unittest.TestCase):
         self.assertEqual(len(mv), self.mem.size)
 
         # Test slicing
-        to_chr = lambda c: chr(c) if tests_base.IS_PY3 else c  # noqa E731 (silence flake8)
-        to_ord = lambda c: ord(c) if tests_base.IS_PY3 else c  # noqa E731 (silence flake8)
-        self.assertEqual([to_chr(c) for c in mv[3:6]],
-                         ['d', 'e', 'f'])
+        self.assertEqual([chr(c) for c in mv[3:6]], ['d', 'e', 'f'])
 
         # Test writing to the memoryview
-        mv[4] = to_ord('x')
+        mv[4] = ord('x')
 
-        self.assertEqual([to_chr(c) for c in mv[3:6]],
-                         ['d', 'x', 'f'])
+        self.assertEqual([chr(c) for c in mv[3:6]], ['d', 'x', 'f'])
 
         # Ensure changes to the underlying segment are reflected in the memoryview
         self.mem.write(b'xxx')
-        self.assertEqual([to_chr(c) for c in mv[:6]],
-                         ['x', 'x', 'x', 'd', 'x', 'f'])
+        self.assertEqual([chr(c) for c in mv[:6]], ['x', 'x', 'x', 'd', 'x', 'f'])
 
 
 if __name__ == '__main__':
diff --git a/tests/test_message_queues.py b/tests/test_message_queues.py
index 650465c..d65fb20 100644
--- a/tests/test_message_queues.py
+++ b/tests/test_message_queues.py
@@ -1,23 +1,19 @@
 # Python imports
-# Don't add any from __future__ imports here. This code should execute
-# against standard Python.
 import unittest
 import time
 import os
 import numbers
+import sys
 
 # Project imports
 import sysv_ipc
-# Hack -- add tests directory to sys.path so Python 3 can find base.py.
-import sys
-sys.path.insert(0, os.path.join(os.getcwd(), 'tests'))  # noqa - tell flake8 to chill
-import base as tests_base
+from .base import Base, make_key, sleep_past_granularity
 
 # Not tested --
 # - mode seems to be settable and readable, but ignored by the OS
 
 
-class MessageQueueTestBase(tests_base.Base):
+class MessageQueueTestBase(Base):
     """base class for MessageQueue test classes"""
     def setUp(self):
         self.mq = sysv_ipc.MessageQueue(None, sysv_ipc.IPC_CREX)
@@ -28,8 +24,7 @@ class MessageQueueTestBase(tests_base.Base):
 
     def assertWriteToReadOnlyPropertyFails(self, property_name, value):
         """test that writing to a readonly property raises TypeError"""
-        tests_base.Base.assertWriteToReadOnlyPropertyFails(self, self.mq,
-                                                           property_name, value)
+        Base.assertWriteToReadOnlyPropertyFails(self, self.mq, property_name, value)
 
 
 class TestMessageQueueCreation(MessageQueueTestBase):
@@ -50,7 +45,7 @@ class TestMessageQueueCreation(MessageQueueTestBase):
     def test_IPC_CREAT_new(self):
         """tests sysv_ipc.IPC_CREAT to create a new MessageQueue without IPC_EXCL"""
         # I can't pass None for the name unless I also pass IPC_EXCL.
-        key = tests_base.make_key()
+        key = make_key()
 
         # Note: this method of finding an unused key is vulnerable to a race
         # condition. It's good enough for test, but don't copy it for use in
@@ -64,7 +59,7 @@ class TestMessageQueueCreation(MessageQueueTestBase):
             except sysv_ipc.ExistentialError:
                 key_is_available = True
             else:
-                key = tests_base.make_key()
+                key = make_key()
 
         mq = sysv_ipc.MessageQueue(key, sysv_ipc.IPC_CREAT)
 
@@ -289,7 +284,7 @@ class TestMessageQueuePropertiesAndAttributes(MessageQueueTestBase):
         # Note that last_change_time doesn't start out as 0 (unlike e.g. last_receive_time), so
         # I don't test that here.
         original_last_change_time = self.mq.last_change_time
-        tests_base.sleep_past_granularity()
+        sleep_past_granularity()
         # This might seem like a no-op, but setting the UID to any value triggers a call that
         # should set last_change_time.
         self.mq.uid = self.mq.uid
diff --git a/tests/test_module.py b/tests/test_module.py
index 4889e08..fadf6cc 100644
--- a/tests/test_module.py
+++ b/tests/test_module.py
@@ -1,23 +1,19 @@
 # Python imports
-# Don't add any from __future__ imports here. This code should execute
-# against standard Python.
 import unittest
 import os
 import resource
 import warnings
 import numbers
+import tempfile
 
 # Project imports
 import sysv_ipc
-# Hack -- add tests directory to sys.path so Python 3 can find base.py.
-import sys
-sys.path.insert(0, os.path.join(os.getcwd(), 'tests'))  # noqa - tell flake8 to chill
-import base as tests_base
+from .base import Base
 
 ONE_MILLION = 1000000
 
 
-class TestModuleConstants(tests_base.Base):
+class TestModuleConstants(Base):
     """Check that the sysv_ipc module-level constants are defined as expected"""
     def test_constant_values(self):
         """test that constants are what I expect"""
@@ -45,7 +41,7 @@ class TestModuleConstants(tests_base.Base):
         self.assertIsInstance(sysv_ipc.__copyright__, str)
 
 
-class TestModuleErrors(tests_base.Base):
+class TestModuleErrors(Base):
     """Exercise the exceptions defined by the module"""
     def test_errors(self):
         self.assertTrue(issubclass(sysv_ipc.Error, Exception))
@@ -56,7 +52,7 @@ class TestModuleErrors(tests_base.Base):
         self.assertTrue(issubclass(sysv_ipc.NotAttachedError, sysv_ipc.Error))
 
 
-class TestModuleFunctions(tests_base.Base):
+class TestModuleFunctions(Base):
     """Exercise the sysv_ipc module-level functions"""
     def test_attach(self):
         """Exercise attach()"""
@@ -127,6 +123,14 @@ class TestModuleFunctions(tests_base.Base):
         """Ensure ftok() returns an int"""
         self.assertIsInstance(sysv_ipc.ftok('.', 42, silence_warning=True), numbers.Integral)
 
+    def test_ftok_raises_os_error(self):
+        """Ensure ftok() failure raises an exception"""
+        with tempfile.TemporaryDirectory() as tmp_dir_name:
+            # Create a path that should cause ftok() to fail.
+            does_not_exist_path = os.path.join(tmp_dir_name, "does_not_exist")
+            with self.assertRaises(OSError):
+                sysv_ipc.ftok(does_not_exist_path, 42, silence_warning=True)
+
     def test_remove_semaphore(self):
         """Exercise remove_semaphore()"""
         sem = sysv_ipc.Semaphore(None, sysv_ipc.IPC_CREX)
diff --git a/tests/test_semaphores.py b/tests/test_semaphores.py
index 2540992..4dd87e2 100644
--- a/tests/test_semaphores.py
+++ b/tests/test_semaphores.py
@@ -1,6 +1,4 @@
 # Python imports
-# Don't add any from __future__ imports here. This code should execute
-# against standard Python.
 import unittest
 import datetime
 import time
@@ -8,10 +6,7 @@ import os
 
 # Project imports
 import sysv_ipc
-# Hack -- add tests directory to sys.path so Python 3 can find base.py.
-import sys
-sys.path.insert(0, os.path.join(os.getcwd(), 'tests'))  # noqa - tell flake8 to chill
-import base as tests_base
+from .base import Base, make_key
 
 # Not tested --
 # - mode seems to be settable and readable, but ignored by the OS
@@ -28,7 +23,7 @@ import base as tests_base
 N_RELEASES = 1000000  # 1 million
 
 
-class SemaphoreTestBase(tests_base.Base):
+class SemaphoreTestBase(Base):
     """base class for Semaphore test classes"""
     def setUp(self):
         self.sem = sysv_ipc.Semaphore(None, sysv_ipc.IPC_CREX, initial_value=1)
@@ -39,7 +34,7 @@ class SemaphoreTestBase(tests_base.Base):
 
     def assertWriteToReadOnlyPropertyFails(self, property_name, value):
         """test that writing to a readonly property raises TypeError"""
-        tests_base.Base.assertWriteToReadOnlyPropertyFails(self, self.sem, property_name, value)
+        Base.assertWriteToReadOnlyPropertyFails(self, self.sem, property_name, value)
 
     def assertDeltasCloseEnough(self, delta_a, delta_b):
         """Compare two datetime.timedeltas and ensure they're within < 1 second of one another.
@@ -75,7 +70,7 @@ class TestSemaphoreCreation(SemaphoreTestBase):
     def test_IPC_CREAT_new(self):
         """tests sysv_ipc.IPC_CREAT to create a new semaphore without IPC_EXCL"""
         # I can't pass None for the name unless I also pass IPC_EXCL.
-        key = tests_base.make_key()
+        key = make_key()
 
         # Note: this method of finding an unused key is vulnerable to a race
         # condition. It's good enough for test, but don't copy it for use in
@@ -88,7 +83,7 @@ class TestSemaphoreCreation(SemaphoreTestBase):
             except sysv_ipc.ExistentialError:
                 key_is_available = True
             else:
-                key = tests_base.make_key()
+                key = make_key()
 
         sem = sysv_ipc.Semaphore(key, sysv_ipc.IPC_CREAT)
 

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/lib/debug/.build-id/4e/945a5df23b09b9b66a79a6e8fd85022c1dfa16.debug
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/sysv_ipc-1.1.0.egg-info/PKG-INFO
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/sysv_ipc-1.1.0.egg-info/dependency_links.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/sysv_ipc-1.1.0.egg-info/top_level.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/sysv_ipc.cpython-310-x86_64-linux-gnu.so

Files in first set of .debs but not in second

-rw-r--r--  root/root   /usr/lib/debug/.build-id/2a/d3191ed97151f671c92b2045d681c9ac21f21e.debug
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/sysv_ipc-1.0.0.egg-info/PKG-INFO
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/sysv_ipc-1.0.0.egg-info/dependency_links.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/sysv_ipc-1.0.0.egg-info/top_level.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/sysv_ipc.cpython-311-x86_64-linux-gnu.so

Control files of package python3-sysv-ipc: lines which differ (wdiff format)

  • Depends: python3 (<< 3.12), 3.11), python3 (>= 3.11~), 3.10~), libc6 (>= 2.14)

Control files of package python3-sysv-ipc-dbgsym: lines which differ (wdiff format)

  • Build-Ids: 2ad3191ed97151f671c92b2045d681c9ac21f21e 4e945a5df23b09b9b66a79a6e8fd85022c1dfa16

More details

Full run details