diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..d1dcbb8
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,24 @@
+AUTHORS / CONTRIBUTORS:
+
+ * Steve Clement -- https://github.com/SteveClement
+ * FliegendeWurst -- https://github.com/FliegendeWurst
+ * Cowles, Matthew Dixon -- ftp://ftp.visi.com/users/mdc/ping.py
+ * Diemer, Jens -- http://www.jensdiemer.de
+ * Esteban, Mendoza
+ * Falatic, Martin -- http://www.falatic.com
+ * Hallman, Chris -- http://cdhallman.blogspot.com
+ * incidence -- https://github.com/incidence
+ * jcborras -- https://github.com/jcborras
+ * Kolev, Georgi -- https://github.com/l4m3rx
+ * Notaras, George -- http://www.g-loaded.eu
+ * Poincheval, Jerome
+ * Sarkhel, Kunal -- https://github.com/techwizrd
+ * Stauffer, Samuel -- https://github.com/samuel
+ * Zach Ware
+ * zed -- https://github.com/zed
+ * Frassinelli, Francesco -- http://www.frafra.eu
+ * Estemendoza, Esteban -- https://github.com/estemendoza
+ * Auke Willem
+ * Tom Wilkie -- http://hithub.com/tomwilkie
+ * simudream -- https://github.com/simudream
+ * Ariana Giroux -- https://github.com/eclectickmedia
diff --git a/ChangeLog.creole b/ChangeLog.creole
new file mode 100644
index 0000000..2447ab3
--- /dev/null
+++ b/ChangeLog.creole
@@ -0,0 +1,202 @@
+== Revision history ==
+
+==== November 4, 2016 ====
+----------------
+ * Renaming MyStats class to avoid confusion
+ * Making myStats global variable so we can print stats when terminating with signal
+ * Fixing small bug in quiet_ping() preventing in from ever exiting.
+
+==== November 3, 2016 ====
+----------------
+ * Converting quiet and verbose ping functions to generator so we can do infinite ping.
+ All the work was done by Ariana Giroux.
+ https://github.com/l4m3rx/python-ping/issues/14
+
+==== November 3, 2016 ====
+----------------
+ * Adding exception when sending packets to handle socket.error (Python2.x issue)
+ Thanks to Ariana Giroux https://github.com/l4m3rx/python-ping/issues/16
+ * Fixing MyStats printing.
+
+==== November 2, 2016 ====
+----------------
+ * Fix. Replacing print() with print('') to avoid python2.x printing "()" liens
+ * Introduce a basic CLI interface via Python's argparse by Ariana Giroux
+ https://github.com/l4m3rx/python-ping/pull/15/commits/0ea79021dfba8ce57ee967f0e923846f627aac71
+ * Rename variables to avoid overwriting python building functions/objects.
+ ping.py: bytes -> _bytes
+ setup.py: hash -> mhash
+ icmp_messages.py: type -> itype
+
+==== November 2, 2016 ====
+----------------
+ * Updating Authors
+ * Fixing up function names as suggested by EclectickMedia
+ https://github.com/l4m3rx/python-ping/issues/13
+
+==== October 26, 2016 ====
+----------------
+ * PEP8 Stuff
+ * Code clean up
+ * Remove revision history from ping.py
+
+==== October 25, 2016 ====
+----------------
+ * New unittest (tests.py) thanks to EclectickMedia
+ * Switching .iteritems() to .items() in icmp_messages.py for better competability (thanks to Steve Clement)
+ * Small fixes
+
+==== October 24, 2016 ====
+----------------
+ * Python 3 competability
+ * Fixing small bug (verbose ping printing)
+
+==== February 4, 2015 ====
+----------------
+ * Fix statistics dump on unexpected exit
+ * Fixing a bug where we match the ICMP request as respons
+ - Fix based on samuel's work https://github.com/samuel/python-ping/commit/745c159dacce6afebb0f82cac8e9ed5bb2189491#diff-0ee81781c0132dc8f743df3a41b71918R128
+ * Merging simudream's patch to fix python3 error handeling.
+
+
+==== January 21, 2015 ====
+----------------
+* Set socket options to allow sending pings to broadcast address
+ - Based on Tom Wilkie's patch
+ https://github.com/tomwilkie/pyping/commit/7d017b03ee462fae72e2d0ff96a042bcba295564
+
+==== August 6, 2014 ====
+--------------
+* Fixing an bug introduced with last changes :S
+ - Python on Windows dosn't have socket.inet_ntop
+ Adding try/except to handle AttributeError.
+
+==== June 30, 2014 ====
+-------------
+* For problems/suggestions https://github.com/l4m3rx/python-ping/issues
+
+* Forgot to add stuff to this README :p
+
+==== June 29, 2014 ====
+-------------
+* Merging parts of code from all the python-ping forks on github
+ * Litlle modifications by Auke Willem Oosterhoff:
+ - Added support for simultaneous pings on multiple hosts.
+ https://bitbucket.org/OrangeTux/python-ping/commits/d4aa720662995a57cf18fa6b8ea689e9d11d26c7/raw/
+
+ * Faster and cleaner checksum creation
+ Based on frfra's patch
+ https://github.com/alexlouden/python-ping/commit/b9fc3acb2c36ccc895d1f7ba7336b951dc033ce9
+
+ * Changing 'except' calls to work with 2.x and 3.x
+ Based on pferate's patch
+ https://github.com/pferate/python_ping/commit/4e761ea99582ac1699c7965d149ce16e6b62f0ac
+
+* Some small PIP8 stuff...
+
+* Merging stuff from/with https://github.com/estemendoza/python-ping
+ Both repos are in sync now.
+
+==== June 19, 2013 ====
+--------------
+* Added support for IPv6. Taken from implementation of Lars Strand.
+
+==== March 19, 2013 ====
+--------------
+* Fixing bug to prevent divide by 0 during run-time.
+
+==== January 26, 2012 ====
+----------------
+* Fixing BUG #4 - competability with python 2.x [tested with 2.7]
+- Packet data building is different for 2.x and 3.x.
+'cose of the string/bytes difference.
+* Fixing BUG #10 - the multiple resolv issue.
+- When pinging domain names insted of hosts (for exmaple google.com)
+you can get different IP every time you try to resolv it, we should
+resolv the host only once and stick to that IP.
+* Fixing BUGs #3 #10 - Doing hostname resolv only once.
+* Fixing BUG #14 - Removing all 'global' stuff.
+- You should not use globul! Its bad for you...and its not thread safe!
+* Fix - forcing the use of different times on linux/windows for
+more accurate mesurments. (time.time - linux/ time.clock - windows)
+* Adding quiet_ping function - This way we'll be able to use this script
+as external lib.
+* Changing default timeout to 3s. (1second is not enought)
+* Switching data syze to packet size. It's easyer for the user to ignore the
+fact that the packet headr is 8b and the datasize 64 will make packet with
+
+==== Oct. 17, 2011 ====
+--------------
+* [[https://github.com/jedie/python-ping/pull/6|Bugfix if host is unknown]]
+
+==== Oct. 12, 2011 ====
+--------------
+Merge sources and create a seperate github repository:
+* https://github.com/jedie/python-ping
+
+Add a simple CLI interface.
+
+==== September 12, 2011 ====
+--------------
+* Bugfixes + cleanup by Jens Diemer
+ Tested with Ubuntu + Windows 7
+
+==== September 6, 2011 ====
+--------------
+* [[http://www.falatic.com/index.php/39/pinging-with-python|Cleanup by Martin Falatic.]]
+Restored lost comments and docs. Improved functionality: constant time between
+pings, internal times consistently use milliseconds. Clarified annotations
+(e.g., in the checksum routine). Using unsigned data in IP & ICMP header
+pack/unpack unless otherwise necessary. Signal handling. Ping-style output
+formatting and stats.
+
+==== August 3, 2011 ====
+--------------
+* Ported to py3k by Zach Ware. Mostly done by 2to3; also minor changes to
+ deal with bytes vs. string changes (no more ord() in checksum() because
+ >source_string< is actually bytes, added .encode() to data in
+ send_one_ping()). That's about it.
+
+==== March 11, 2010 ====
+--------------
+* changes by Samuel Stauffer:
+ replaced time.clock with default_timer which is set to
+ time.clock on windows and time.time on other systems.
+
+==== November 8, 2009 ====
+--------------
+* Fixes by [[http://www.g-loaded.eu/2009/10/30/python-ping/|George Notaras]],
+ reported by [[http://cdhallman.blogspot.com|Chris Hallman]]:
+
+* Improved compatibility with GNU/Linux systems.
+
+* Changes in this release:
+ Re-use time.time() instead of time.clock(). The 2007 implementation
+ worked only under Microsoft Windows. Failed on GNU/Linux.
+ time.clock() behaves differently under [[http://docs.python.org/library/time.html#time.clock|the two OSes]].
+
+
+==== May 30, 2007 ====
+--------------
+little [[http://www.python-forum.de/post-69122.html#69122|rewrite by Jens Diemer]]:
+ * change socket asterisk import to a normal import
+ * replace time.time() with time.clock()
+ * delete "return None" (or change to "return" only)
+ * in checksum() rename "str" to "source_string"
+
+==== December 4, 2000 ====
+--------------
+* Changed the struct.pack() calls to pack the checksum and ID as
+ unsigned. My thanks to Jerome Poincheval for the fix.
+
+==== November 22, 1997 ====
+--------------
+* Initial hack. Doesn't do much, but rather than try to guess
+ what features I (or others) will want in the future, I've only
+ put in what I need now.
+
+==== December 16, 1997 ====
+For some reason, the checksum bytes are in the wrong order when
+this is run under Solaris 2.X for SPARC but it works right under
+Linux x86. Since I don't know just what's wrong, I'll swap the
+bytes always and then do an htons().
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..fbb1510
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,35 @@
+A pure python implementation of netkit's ping.c
+
+Copyright (C) 2016, python-ping team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+To report a change or a bug, open an issue on
+https://github.com/l4m3rx/python-ping or email one of the contributors.
+
+-------------------------------------------------------------------------------
+PREVIOUS LICENSING STATEMENT.
+
+The original code derived from ping.c distributed in Linux's netkit.
+That code is copyright (c) 1989 by The Regents of the University of California.
+That code is in turn derived from code written by Mike Muuss of the
+US Army Ballistic Research Laboratory in December, 1983 and
+placed in the public domain. They have my thanks.
+
+Copyright (c) Matthew Dixon Cowles, .
+Distributable under the terms of the GNU General Public License
+version 2. Provided with no warranties of any sort.
+
+See AUTHORS for complete list of authors and contributors.
diff --git a/README b/README
deleted file mode 100644
index 1488af0..0000000
--- a/README
+++ /dev/null
@@ -1,2 +0,0 @@
-Originally from http://svn.pylucid.net/pylucid/CodeSnippets/ping.py
-This version maintained at http://github.com/samuel/python-ping
\ No newline at end of file
diff --git a/README.creole b/README.creole
new file mode 100644
index 0000000..d39be02
--- /dev/null
+++ b/README.creole
@@ -0,0 +1,84 @@
+== python-ping ==
+A pure python ping implementation using raw sockets.
+ * Compatible with Python2 & Python3
+ * Note that ICMP messages can only be sent from processes running as root
+ (in Windows, you must run this script as 'Administrator').
+
+== Original Version ==
+* [[ftp://ftp.visi.com/users/mdc/ping.py|Matthew Dixon Cowles]]
+* copyleft 1989-2016 by the python-ping team, see [[https://github.com/l4m3rx/python-ping/blob/master/AUTHORS|AUTHORS]] for more details.
+* license: GNU GPL v2, see [[https://github.com/l4m3rx/python-ping/blob/master/LICENSE|LICENSE]] for more details.
+
+=== Usage ===
+
+{{{
+$ sudo python3 ping.py -h
+usage: python-ping [-h] [-t TIMEOUT] [-c REQUEST_COUNT] [-i] [-I]
+ [-s PACKET_SIZE] [-T]
+ address
+
+A pure python implementation of the ping protocol. *REQUIRES ROOT*
+
+positional arguments:
+ address The address to attempt to ping.
+
+optional arguments:
+ -h, --help show this help message and exit
+ -t TIMEOUT, --timeout TIMEOUT
+ The maximum amount of time to wait until ping timeout.
+ -c REQUEST_COUNT, --request_count REQUEST_COUNT
+ The number of attempts to make. See --infinite to
+ attempt requests until stopped.
+ -i, --infinite Flag to continuously ping a host until stopped.
+ -I, --ipv6 Flag to use IPv6.
+ -s PACKET_SIZE, --packet_size PACKET_SIZE
+ Designate the amount of data to send per packet.
+ -T, --test_case Flag to run the default test case suite.
+}}}
+
+
+=== Using as lib ===
+{{{
+Python 2.7.11 (default, Mar 3 2016, 13:35:30)
+[GCC 5.3.0] on linux2
+Type "help", "copyright", "credits" or "license" for more information.
+>>> import ping
+>>> ping.verbose_ping('google.com', timeout=3000, count=3, numDataBytes=1300)
+
+>>> list(ping.verbose_ping('google.com', timeout=3000, count=3, numDataBytes=1300))
+
+PYTHON PING google.com (216.58.212.46): 1300 data bytes
+72 bytes from 216.58.212.46: icmp_seq=0 ttl=59 time=4.42 ms
+72 bytes from 216.58.212.46: icmp_seq=1 ttl=59 time=4.70 ms
+72 bytes from 216.58.212.46: icmp_seq=2 ttl=59 time=4.44 ms
+72 bytes from 216.58.212.46: icmp_seq=3 ttl=59 time=4.47 ms
+
+----216.58.212.46 PYTHON PING Statistics----
+4 packets transmitted, 4 packets received, 0.0% packet loss
+round-trip (ms) min/avg/max = 4.4/4.5/4.7
+
+[1, 2, 3, False]
+
+
+>>> list(ping.quiet_ping('google.com', timeout=3000, count=3, numDataBytes=1300))
+[1, 2, 3, (4.508256912231445, 4.332065582275391, 4.399617513020833, 0.0)]
+>>> list(ping.quiet_ping('google.com', timeout=3000, count=3, numDataBytes=1300))[-1]
+(4.535675048828125, 4.359245300292969, 4.423936208089192, 0.0)
+
+}}}
+
+== TODOs ==
+ * Make delay between sending packets as input parm.
+
+== contribute ==
+
+[[http://help.github.com/fork-a-repo/|Fork this repo]] on [[https://github.com/l4m3rx/python-ping/|GitHub]] and [[http://help.github.com/send-pull-requests/|send pull requests]]. Thank you.
+
+
+== Revision history ==
+[[https://github.com/l4m3rx/python-ping/blob/master/ChangeLog.creole|ChangeLog / Revision history]]
+
+
+== Links ==
+
+| Sourcecode at GitHub | https://github.com/l4m3rx/python-ping |
diff --git a/__init__.py b/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/icmp_messages.py b/icmp_messages.py
new file mode 100755
index 0000000..e687c07
--- /dev/null
+++ b/icmp_messages.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+ ICMP Control Messages
+ https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol#Control_messages
+ ICMP Types 0, 3, 4, 5, 8, 11, 12, 13, 14 from RFC792
+ ICMP Types 9, 11 from RFC1256
+"""
+ICMP_CONTROL_MESSAGE = \
+ {0: {0: 'Echo reply',
+ },
+ 3: {0: 'Destination network unreachable',
+ 1: 'Destination host unreachable',
+ 2: 'Destination protocol unreachable',
+ 3: 'Destination port unreachable',
+ 4: 'Fragmentation required, and DF flag set',
+ 5: 'Source route failed',
+ 6: 'Destination network unknown',
+ 7: 'Destination host unknown',
+ 8: 'Source host isolated',
+ 9: 'Network administratively prohibited',
+ 10: 'Host administratively prohibited',
+ 11: 'Network unreachable for TOS',
+ 12: 'Host unreachable for TOS',
+ 13: 'Communication administratively prohibited',
+ 14: 'Host Precedence Violation',
+ 15: 'Precedence cutoff in effect',
+ },
+ 4: {0: 'Source quench',
+ },
+ 5: {0: 'Redirect Datagram for the Network',
+ 1: 'Redirect Datagram for the Host',
+ 2: 'Redirect Datagram for the TOS & network',
+ 3: 'Redirect Datagram for the TOS & host',
+ },
+ 8: {0: 'Echo request',
+ },
+ 9: {0: 'Router Advertisement',
+ },
+ 10:{0: 'Router discovery/selection/solicitation',
+ },
+ 11:{0: 'TTL expired in transit',
+ 1: 'Fragment reassembly time exceeded',
+ },
+ 12:{0: 'Pointer indicates the error',
+ 1: 'Missing a required option',
+ 2: 'Bad length',
+ },
+ 13:{0: 'Timestamp',
+ },
+ 14:{0: 'Timestamp reply',
+ },
+ }
+
+
+"""
+ ICMPv6 Control Messages
+ https://en.wikipedia.org/wiki/ICMPv6#Types_of_ICMPv6_messages
+ ICMPv6 Types 0-127 are Error Messages
+ ICMPv6 Types 128-255 are Informational Messages
+"""
+ICMPv6_CONTROL_MESSAGE = \
+ {1: {0: 'no route to destination',
+ 1: 'communication with destination administratively prohibited',
+ 2: 'beyond scope of source address',
+ 3: 'address unreachable',
+ 4: 'port unreachable',
+ 5: 'source address failed ingress/egress policy',
+ 6: 'reject route to destination',
+ 7: 'Error in Source Routing Header',
+ },
+ 2: {0: 'packet too big',
+ },
+ 3: {0: 'hop limit exceeded in transit',
+ 1: 'fragment reassembly time exceeded',
+ },
+ 4: {0: 'erroneous header field encountered',
+ 1: 'unrecognized Next Header type encountered',
+ 2: 'unrecognized IPv6 option encountered',
+ },
+ }
+
+if __name__ == '__main__':
+ # Print all defined ICMP Control Messages
+ print("ICMP Control Messages")
+ print("Type\tCode:\tMessage")
+ for (itype, codes) in ICMP_CONTROL_MESSAGE.items():
+ print("")
+ for (code, message) in codes.items():
+ print("[%d]\t[%d]:\t%s" % (itype, code, message))
+ print("")
+
+ # Print all defined ICMPv6 Control Messages
+ print("ICMPv6 Control Messages")
+ print("Type\tCode:\tMessage")
+ for (itype, codes) in ICMPv6_CONTROL_MESSAGE.items():
+ print("")
+ for (code, message) in codes.items():
+ print("[%d]\t[%d]:\t%s" % (itype, code, message))
diff --git a/ping.py b/ping.py
old mode 100644
new mode 100755
index 73400fa..7694a14
--- a/ping.py
+++ b/ping.py
@@ -1,11 +1,17 @@
#!/usr/bin/env python
+# -*- coding: utf-8 -*-
-"""
- A pure python ping implementation using raw socket.
+from __future__ import division, print_function
+"""
+ A pure python ping implementation using raw sockets.
- Note that ICMP messages can only be sent from processes running as root.
+ Compatibility:
+ OS: Linux, Windows, MacOSX
+ Python: 2.6 - 3.5
+ Note that due to the usage of RAW sockets root/Administrator
+ privileges are requied.
Derived from ping.c distributed in Linux's netkit. That code is
copyright (c) 1989 by The Regents of the University of California.
@@ -13,63 +19,34 @@
US Army Ballistic Research Laboratory in December, 1983 and
placed in the public domain. They have my thanks.
- Bugs are naturally mine. I'd be glad to hear about them. There are
- certainly word - size dependenceies here.
-
Copyright (c) Matthew Dixon Cowles, .
Distributable under the terms of the GNU General Public License
version 2. Provided with no warranties of any sort.
- Original Version from Matthew Dixon Cowles:
- -> ftp://ftp.visi.com/users/mdc/ping.py
-
- Rewrite by Jens Diemer:
- -> http://www.python-forum.de/post-69122.html#69122
-
-
- Revision history
- ~~~~~~~~~~~~~~~~
-
- March 11, 2010
- changes by Samuel Stauffer:
- - replaced time.clock with default_timer which is set to
- time.clock on windows and time.time on other systems.
-
- May 30, 2007
- little rewrite by Jens Diemer:
- - change socket asterisk import to a normal import
- - replace time.time() with time.clock()
- - delete "return None" (or change to "return" only)
- - in checksum() rename "str" to "source_string"
-
- November 22, 1997
- Initial hack. Doesn't do much, but rather than try to guess
- what features I (or others) will want in the future, I've only
- put in what I need now.
-
- December 16, 1997
- For some reason, the checksum bytes are in the wrong order when
- this is run under Solaris 2.X for SPARC but it works right under
- Linux x86. Since I don't know just what's wrong, I'll swap the
- bytes always and then do an htons().
+ website: https://github.com/l4m3rx/python-ping
- December 4, 2000
- Changed the struct.pack() calls to pack the checksum and ID as
- unsigned. My thanks to Jerome Poincheval for the fix.
+"""
- Januari 27, 2015
- Changed receive response to not accept ICMP request messages.
- It was possible to receive the very request that was sent.
+# TODO Remove any calls to time.sleep
+# This would enable extension into larger framework that aren't multi threaded.
+import os
+import sys
+import time
+import array
+import socket
+import struct
+import select
+import signal
- Last commit info:
- ~~~~~~~~~~~~~~~~~
- $LastChangedDate: $
- $Rev: $
- $Author: $
-"""
+if __name__ == '__main__':
+ import argparse
-import os, sys, socket, struct, select, time
+try:
+ from _thread import get_ident
+except ImportError:
+ def get_ident():
+ return 0
if sys.platform == "win32":
# On Windows, the best timer is time.clock()
@@ -78,146 +55,612 @@
# On most other platforms the best timer is time.time()
default_timer = time.time
-# From /usr/include/linux/icmp.h; your milage may vary.
-ICMP_ECHO_REQUEST = 8 # Seems to be the same on Solaris.
+# ICMP parameters
+
+ICMP_ECHOREPLY = 0 # Echo reply (per RFC792)
+ICMP_ECHO = 8 # Echo request (per RFC792)
+ICMP_ECHO_IPV6 = 128 # Echo request (per RFC4443)
+ICMP_ECHO_IPV6_REPLY = 129 # Echo request (per RFC4443)
+ICMP_MAX_RECV = 2048 # Max size of incoming buffer
+
+MAX_SLEEP = 1000
+
+
+class MStats2(object):
+
+ def __init__(self):
+ self._this_ip = '0.0.0.0'
+ self.reset()
+
+ def reset(self):
+ self._timing_list = []
+ self._packets_sent = 0
+ self._packets_rcvd = 0
+
+ self._reset_statistics()
+
+ @property
+ def thisIP(self):
+ return self._this_ip
+
+ @thisIP.setter
+ def thisIP(self, value):
+ self._this_ip = value
+
+ @property
+ def pktsSent(self):
+ return self._packets_sent
+
+ @property
+ def pktsRcvd(self):
+ return self._packets_rcvd
+
+ @property
+ def pktsLost(self):
+ return self._packets_sent - self._packets_rcvd
+
+ @property
+ def minTime(self):
+ return min(self._timing_list) if self._timing_list else None
+
+ @property
+ def maxTime(self):
+ return max(self._timing_list) if self._timing_list else None
+
+ @property
+ def totTime(self):
+ if self._total_time is None:
+ self._total_time = sum(self._timing_list)
+ return self._total_time
+
+ def _get_mean_time(self):
+ if self._mean_time is None:
+ if len(self._timing_list) > 0:
+ self._mean_time = self.totTime / len(self._timing_list)
+ return self._mean_time
+ mean_time = property(_get_mean_time)
+ avrgTime = property(_get_mean_time)
+
+ @property
+ def median_time(self):
+ if self._median_time is None:
+ self._median_time = self._calc_median_time()
+ return self._median_time
+
+ @property
+ def pstdev_time(self):
+ """Returns the 'Population Standard Deviation' of the set."""
+ if self._pstdev_time is None:
+ self._pstdev_time = self._calc_pstdev_time()
+ return self._pstdev_time
+
+ @property
+ def fracLoss(self):
+ if self._frac_loss is None:
+ if self.pktsSent > 0:
+ self._frac_loss = self.pktsLost / self.pktsSent
+ return self._frac_loss
+
+ def packet_sent(self, n=1):
+ self._packets_sent += n
+
+ def packet_received(self, n=1):
+ self._packets_rcvd += n
+
+ def record_time(self, value):
+ self._timing_list.append(value)
+ self._reset_statistics()
+
+ def _reset_statistics(self):
+ self._total_time = None
+ self._mean_time = None
+ self._median_time = None
+ self._pstdev_time = None
+ self._frac_loss = None
+
+ def _calc_median_time(self):
+ n = len(self._timing_list)
+ if n == 0:
+ return None
+ if n & 1 == 1: # Odd number of samples? Return the middle.
+ return sorted(self._timing_list)[n // 2]
+ # Even number of samples? Return the mean of the two middle samples.
+ else:
+ halfn = n // 2
+ return sum(sorted(self._timing_list)[halfn:halfn + 2]) / 2
+ def _calc_sum_square_time(self):
+ mean = self.mean_time
+ return sum(((t - mean)**2 for t in self._timing_list))
-def checksum(source_string):
- """
- I'm not too confident that this is right but testing seems
- to suggest that it gives the same answers as in_cksum in ping.c
- """
- sum = 0
- countTo = (len(source_string)/2)*2
- count = 0
- while count> 16) + (sum & 0xffff)
- sum = sum + (sum >> 16)
- answer = ~sum
- answer = answer & 0xffff
-
- # Swap bytes. Bugger me if I know why.
- answer = answer >> 8 | (answer << 8 & 0xff00)
+ def _calc_pstdev_time(self):
+ pvar = self._calc_sum_square_time() / len(self._timing_list)
+ return pvar**0.5
- return answer
+
+# Used as 'global' variale so we can print
+# stats when exiting by signal
+myStats = MStats2()
-def receive_one_ping(my_socket, ID, timeout):
+def _checksum(source_string):
"""
- receive the ping from the socket.
+ A port of the functionality of in_cksum() from ping.c
+ Ideally this would act on the string as a series of 16-bit ints (host
+ packed), but this works.
+ Network data is big-endian, hosts are typically little-endian
"""
- timeLeft = timeout
- while True:
- startedSelect = default_timer()
- whatReady = select.select([my_socket], [], [], timeLeft)
- howLongInSelect = (default_timer() - startedSelect)
- if whatReady[0] == []: # Timeout
- return
+ if (len(source_string) % 2):
+ source_string += "\x00"
+ converted = array.array("H", source_string)
+ if sys.byteorder == "big":
+ converted.bytewap()
+ val = sum(converted)
- timeReceived = default_timer()
- recPacket, addr = my_socket.recvfrom(1024)
- icmpHeader = recPacket[20:28]
- type, code, checksum, packetID, sequence = struct.unpack(
- "bbHHh", icmpHeader
- )
- # Filters out the echo request itself.
- # This can be tested by pinging 127.0.0.1
- # You'll see your own request
- if type != 8 and packetID == ID:
- bytesInDouble = struct.calcsize("d")
- timeSent = struct.unpack("d", recPacket[28:28 + bytesInDouble])[0]
- return timeReceived - timeSent
+ val &= 0xffffffff # Truncate val to 32 bits (a variance from ping.c, which
+ # uses signed ints, but overflow is unlikely in ping)
- timeLeft = timeLeft - howLongInSelect
- if timeLeft <= 0:
- return
+ val = (val >> 16) + (val & 0xffff) # Add high 16 bits to low 16 bits
+ val += (val >> 16) # Add carry from above (if any)
+ answer = ~val & 0xffff # Invert and truncate to 16 bits
+ answer = socket.htons(answer)
+
+ return answer
-def send_one_ping(my_socket, dest_addr, ID):
+def single_ping(destIP, hostname, timeout, mySeqNumber, numDataBytes,
+ myStats=None, ipv6=False, verbose=True, sourceIP=None):
+ """
+ Returns either the delay (in ms) or None on timeout.
"""
- Send one ping to the given >dest_addr<.
+ delay = None
+
+ if ipv6:
+ try: # One could use UDP here, but it's obscure
+ mySocket = socket.socket(socket.AF_INET6, socket.SOCK_RAW,
+ socket.getprotobyname("ipv6-icmp"))
+ if sourceIP is not None:
+ mySocket.bind((sourceIP, 0))
+ mySocket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
+ mySocket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_RECVHOPLIMIT,
+ 1)
+ except OSError as e:
+ if verbose:
+ print("failed. (socket error: '%s')" % str(e))
+ print('Note that python-ping uses RAW sockets'
+ 'and requiers root rights.')
+ raise # raise the original error
+ else:
+
+ try: # One could use UDP here, but it's obscure
+ mySocket = socket.socket(socket.AF_INET, socket.SOCK_RAW,
+ socket.getprotobyname("icmp"))
+ if sourceIP is not None:
+ mySocket.bind((sourceIP, 0))
+ mySocket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
+ except OSError as e:
+ if verbose:
+ print("failed. (socket error: '%s')" % str(e))
+ print('Note that python-ping uses RAW sockets'
+ 'and requires root rights.')
+ raise # raise the original error
+
+ my_ID = (os.getpid() ^ get_ident()) & 0xFFFF
+
+ sentTime = _send(mySocket, destIP, my_ID, mySeqNumber, numDataBytes, ipv6,
+ verbose)
+ if sentTime is None:
+ mySocket.close()
+ return delay, (None,)
+
+ if myStats is not None:
+ myStats.packet_sent()
+
+ recvTime, dataSize, iphSrcIP, icmpSeqNumber, iphTTL \
+ = _receive(mySocket, my_ID, timeout, ipv6)
+
+ mySocket.close()
+
+ if recvTime:
+ delay = (recvTime - sentTime) * 1000
+ if ipv6:
+ host_addr = hostname
+ else:
+ try:
+ host_addr = socket.inet_ntop(socket.AF_INET, struct.pack(
+ "!I", iphSrcIP))
+ except AttributeError:
+ # Python on windows dosn't have inet_ntop.
+ host_addr = hostname
+
+ if verbose:
+ print("%d bytes from %s: icmp_seq=%d ttl=%d time=%.2f ms" % (
+ dataSize, host_addr, icmpSeqNumber, iphTTL, delay)
+ )
+
+ if myStats is not None:
+ assert isinstance(myStats, MStats2)
+ myStats.packet_received()
+ myStats.record_time(delay)
+ else:
+ delay = None
+ if verbose:
+ print("Request timed out.")
+
+ return delay, (recvTime, dataSize, iphSrcIP, icmpSeqNumber, iphTTL)
+
+
+def _send(mySocket, destIP, myID, mySeqNumber, numDataBytes, ipv6=False,
+ verbose=True):
"""
- dest_addr = socket.gethostbyname(dest_addr)
+ Send one ping to the given >destIP<.
+ """
+ # destIP = socket.gethostbyname(destIP)
# Header is type (8), code (8), checksum (16), id (16), sequence (16)
- my_checksum = 0
+ # (numDataBytes - 8) - Remove header size from packet size
+ myChecksum = 0
# Make a dummy heder with a 0 checksum.
- header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, my_checksum, ID, 1)
- bytesInDouble = struct.calcsize("d")
- data = (192 - bytesInDouble) * "Q"
- data = struct.pack("d", default_timer()) + data
+ if ipv6:
+ header = struct.pack(
+ "!BbHHh", ICMP_ECHO_IPV6, 0, myChecksum, myID, mySeqNumber
+ )
+ else:
+ header = struct.pack(
+ "!BBHHH", ICMP_ECHO, 0, myChecksum, myID, mySeqNumber
+ )
+
+ padBytes = []
+ startVal = 0x42
+ # 'cose of the string/byte changes in python 2/3 we have
+ # to build the data differnely for different version
+ # or it will make packets with unexpected size.
+ if sys.version[:1] == '2':
+ _bytes = struct.calcsize("d")
+ data = ((numDataBytes - 8) - _bytes) * "Q"
+ data = struct.pack("d", default_timer()) + data
+ else:
+ for i in range(startVal, startVal + (numDataBytes - 8)):
+ padBytes += [(i & 0xff)] # Keep chars in the 0-255 range
+ # data = bytes(padBytes)
+ data = bytearray(padBytes)
# Calculate the checksum on the data and the dummy header.
- my_checksum = checksum(header + data)
+ myChecksum = _checksum(header + data) # Checksum is in network order
# Now that we have the right checksum, we put that in. It's just easier
# to make up a new header than to stuff it into the dummy.
- header = struct.pack(
- "bbHHh", ICMP_ECHO_REQUEST, 0, socket.htons(my_checksum), ID, 1
- )
+ if ipv6:
+ header = struct.pack(
+ "!BbHHh", ICMP_ECHO_IPV6, 0, myChecksum, myID, mySeqNumber
+ )
+ else:
+ header = struct.pack(
+ "!BBHHH", ICMP_ECHO, 0, myChecksum, myID, mySeqNumber
+ )
+
packet = header + data
- my_socket.sendto(packet, (dest_addr, 1)) # Don't know about the 1
+
+ sendTime = default_timer()
+
+ try:
+ mySocket.sendto(packet, (destIP, 58)) # Port number is irrelevant
+ except OSError as e:
+ if verbose:
+ print("General failure (%s)" % str(e))
+ return
+ except socket.error as e:
+ if verbose:
+ print("General failure (%s)" % str(e))
+ return
+
+ return sendTime
-def do_one(dest_addr, timeout):
+def _receive(mySocket, myID, timeout, ipv6=False):
"""
- Returns either the delay (in seconds) or none on timeout.
+ Receive the ping from the socket. Timeout = in ms
"""
- icmp = socket.getprotobyname("icmp")
- try:
- my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp)
- except socket.error, (errno, msg):
- if errno == 1:
- # Operation not permitted
- msg = msg + (
- " - Note that ICMP messages can only be sent from processes"
- " running as root."
- )
- raise socket.error(msg)
- raise # raise the original error
+ timeLeft = timeout / 1000
+
+ while True: # Loop while waiting for packet or timeout
+ startedSelect = default_timer()
+ whatReady = select.select([mySocket], [], [], timeLeft)
+ howLongInSelect = (default_timer() - startedSelect)
+ if whatReady[0] == []: # Timeout
+ return None, 0, 0, 0, 0
+
+ timeReceived = default_timer()
+
+ iphSrcIP = 0
+ iphDestIP = 0
+ if ipv6:
+ recPacket, ancdata, flags, addr = mySocket.recvmsg(ICMP_MAX_RECV)
+ iphTTL = 0
+ if len(ancdata) == 1:
+ cmsg_level, cmsg_type, cmsg_data = ancdata[0]
+ a = array.array("i")
+ a.frombytes(cmsg_data)
+ iphTTL = a[0]
+ print("ttl: " + str(iphTTL))
+ else:
+ recPacket, addr = mySocket.recvfrom(ICMP_MAX_RECV)
+ ipHeader = recPacket[:20]
+ iphVersion, iphTypeOfSvc, iphLength, iphID, iphFlags, iphTTL, \
+ iphProtocol, iphChecksum, iphSrcIP, iphDestIP = struct.unpack(
+ "!BBHHHBBHII", ipHeader)
+
+ if ipv6:
+ icmpHeader = recPacket[0:8]
+ else:
+ icmpHeader = recPacket[20:28]
+
+ icmpType, icmpCode, icmpChecksum, icmpPacketID, icmpSeqNumber \
+ = struct.unpack("!BBHHH", icmpHeader)
+
+ # Match only the packets we care about
+ if (icmpType != 8) and (icmpPacketID == myID):
+ dataSize = len(recPacket) - 28
+ return timeReceived, (dataSize + 8), iphSrcIP, icmpSeqNumber, \
+ iphTTL
+
+ timeLeft = timeLeft - howLongInSelect
+ if timeLeft <= 0:
+ return None, 0, 0, 0, 0
+
+
+def _dump_stats(myStats):
+ """
+ Show stats when pings are done
+ """
+ print("\n----%s PYTHON PING Statistics----" % (myStats.thisIP))
+
+ print("%d packets transmitted, %d packets received, %0.1f%% packet loss"
+ % (myStats.pktsSent, myStats.pktsRcvd, 100.0 * myStats.fracLoss))
- my_ID = os.getpid() & 0xFFFF
+ if myStats.pktsRcvd > 0:
+ print("round-trip (ms) min/avg/max = %0.1f/%0.1f/%0.1f" % (
+ myStats.minTime, myStats.avrgTime, myStats.maxTime
+ ))
+ print(' median/pstddev = %0.2f/%0.2f' % (
+ myStats.median_time, myStats.pstdev_time
+ ))
- send_one_ping(my_socket, dest_addr, my_ID)
- delay = receive_one_ping(my_socket, my_ID, timeout)
+ print('')
+ return
- my_socket.close()
- return delay
+def _signal_handler(signum, frame):
+ """ Handle exit via signals """
+ global myStats
+ _dump_stats(myStats)
+ print("\n(Terminated with signal %d)\n" % (signum))
+ sys.exit(0)
-def verbose_ping(dest_addr, timeout = 2, count = 4):
+
+def _pathfind_ping(destIP, hostname, timeout, mySeqNumber, numDataBytes,
+ ipv6=None, sourceIP=None):
+ single_ping(destIP, hostname, timeout,
+ mySeqNumber, numDataBytes, ipv6=ipv6, verbose=False,
+ sourceIP=sourceIP)
+ time.sleep(0.5)
+
+
+def verbose_ping(hostname, timeout=3000, count=3,
+ numDataBytes=64, path_finder=False, ipv6=False,
+ sourceIP=None):
"""
- Send >count< ping to >dest_addr< with the given >timeout< and display
+ Send >count< ping to >destIP< with the given >timeout< and display
the result.
+
+ To continuously attempt ping requests, set >count< to None.
+
+ To consume the generator, use the following syntax:
+ >>> import ping
+ >>> for return_val in ping.verbose_ping('google.ca'):
+ pass # COLLECT YIELDS AND PERFORM LOGIC.
+
+ Alternatively, you can consume the generator by using list comprehension:
+ >>> import ping
+ >>> consume = list(ping.verbose_ping('google.ca'))
+
+ Via the same syntax, you can successfully get the exit code via:
+ >>> import ping
+ >>> consume = list(ping.verbose_ping('google.ca'))
+ >>> exit_code = consume[:-1] # The last yield is the exit code.
+ >>> sys.exit(exit_code)
"""
- for i in xrange(count):
- print "ping %s..." % dest_addr,
- try:
- delay = do_one(dest_addr, timeout)
- except socket.gaierror, e:
- print "failed. (socket error: '%s')" % e[1]
+
+ global myStats
+
+ # Handle Ctrl+C
+ signal.signal(signal.SIGINT, _signal_handler)
+ if hasattr(signal, "SIGBREAK"): # Handle Ctrl-Break /Windows/
+ signal.signal(signal.SIGBREAK, _signal_handler)
+
+ myStats = MStats2() # Reset the stats
+ mySeqNumber = 0 # Starting value
+
+ try:
+ if ipv6:
+ info = socket.getaddrinfo(hostname, None)[0]
+ destIP = info[4][0]
+ else:
+ destIP = socket.gethostbyname(hostname)
+ print("\nPYTHON PING %s (%s): %d data bytes" % (hostname, destIP,
+ numDataBytes))
+ except socket.gaierror as e:
+ print("\nPYTHON PING: Unknown host: %s (%s)" % (hostname, str(e)))
+ print('')
+ return
+
+ myStats.thisIP = destIP
+
+ # This will send packet that we don't care about 0.5 seconds before it
+ # starts actually pinging. This is needed in big MAN/LAN networks where
+ # you sometimes loose the first packet. (while the switches find the way)
+ if path_finder:
+ print("PYTHON PING %s (%s): Sending pathfinder ping" %
+ (hostname, destIP))
+ _pathfind_ping(destIP, hostname, timeout,
+ mySeqNumber, numDataBytes, ipv6=ipv6, sourceIP=sourceIP)
+ print()
+
+ i = 0
+ while 1:
+ delay = single_ping(destIP, hostname, timeout, mySeqNumber,
+ numDataBytes, ipv6=ipv6, myStats=myStats,
+ sourceIP=sourceIP)
+ delay = 0 if delay is None else delay[0]
+
+ mySeqNumber += 1
+
+ # Pause for the remainder of the MAX_SLEEP period (if applicable)
+ if (MAX_SLEEP > delay):
+ time.sleep((MAX_SLEEP - delay) / 1000)
+
+ if count is not None and i < count:
+ i += 1
+ yield myStats.pktsRcvd
+ elif count is None:
+ yield myStats.pktsRcvd
+ elif count is not None and i >= count:
break
- if delay == None:
- print "failed. (timeout within %ssec.)" % timeout
+ _dump_stats(myStats)
+ # 0 if we receive at least one packet
+ # 1 if we don't receive any packets
+ yield not myStats.pktsRcvd
+
+
+def quiet_ping(hostname, timeout=3000, count=3, advanced_statistics=False,
+ numDataBytes=64, path_finder=False, ipv6=False, sourceIP=None):
+ """ Same as verbose_ping, but the results are yielded as a tuple """
+ myStats = MStats2() # Reset the stats
+ mySeqNumber = 0 # Starting value
+
+ try:
+ if ipv6:
+ info = socket.getaddrinfo(hostname, None)[0]
+ destIP = info[4][0]
else:
- delay = delay * 1000
- print "get ping in %0.4fms" % delay
- print
+ destIP = socket.gethostbyname(hostname)
+ except socket.gaierror:
+ yield False
+ return
+
+ myStats.thisIP = destIP
+
+ # This will send packet that we don't care about 0.5 seconds before it
+ # starts actually pinging. This is needed in big MAN/LAN networks where
+ # you sometimes loose the first packet. (while the switches find the way)
+ if path_finder:
+ _pathfind_ping(destIP, hostname, timeout,
+ mySeqNumber, numDataBytes, ipv6=ipv6, sourceIP=sourceIP)
+
+ i = 1
+ while 1:
+ delay = single_ping(destIP, hostname, timeout, mySeqNumber,
+ numDataBytes, ipv6=ipv6, myStats=myStats,
+ verbose=False, sourceIP=sourceIP)
+ delay = 0 if delay[0] is None else delay[0]
+
+ mySeqNumber += 1
+ # Pause for the remainder of the MAX_SLEEP period (if applicable)
+ if (MAX_SLEEP > delay):
+ time.sleep((MAX_SLEEP - delay) / 1000)
+
+ yield myStats.pktsSent
+ if count is not None and i < count:
+ i += 1
+ elif count is not None and i >= count:
+ break
+ elif count is not None:
+ yield myStats.pktsSent
+
+ if advanced_statistics:
+ # return tuple(max_rtt, min_rtt, avrg_rtt, percent_lost,
+ # median, pop.std.dev)
+ yield myStats.maxTime, myStats.minTime, myStats.avrgTime,
+ myStats.fracLoss, myStats.median_time, myStats.pstdev_time
+ else:
+ # return tuple(max_rtt, min_rtt, avrg_rtt, percent_lost)
+ yield myStats.maxTime, myStats.minTime, myStats.avrgTime,
+ myStats.fracLoss
if __name__ == '__main__':
- verbose_ping("heise.de")
- verbose_ping("google.com")
- verbose_ping("a-test-url-taht-is-not-available.com")
- verbose_ping("192.168.1.1")
+ # FIXME: Add a real CLI (mostly fixed)
+ if sys.argv.count('-T') or sys.argv.count('--test_case'):
+ print('Running PYTHON PING test case.')
+ # These should work:
+ for val in verbose_ping("127.0.0.1"):
+ pass
+ for val in verbose_ping("8.8.8.8"):
+ pass
+ for val in verbose_ping("heise.de"):
+ pass
+ for val in verbose_ping("google.com"):
+ pass
+
+ # Inconsistent on Windows w/ ActivePython (Python 3.2 resolves
+ # correctly to the local host, but 2.7 tries to resolve to the local
+ # *gateway*)
+ for val in verbose_ping("localhost"):
+ pass
+
+ # Should fail with 'getaddrinfo failed':
+ for val in verbose_ping("foobar_url.fooobar"):
+ pass
+
+ # Should fail (timeout), but it depends on the local network:
+ for val in verbose_ping("192.168.255.254"):
+ pass
+
+ # Should fails with 'The requested address is not valid in its context'
+ for val in verbose_ping("0.0.0.0"):
+ pass
+
+ exit()
+
+ parser = argparse.ArgumentParser(prog='python-ping',
+ description='A pure python implementation\
+ of the ping protocol. *REQUIRES ROOT*')
+ parser.add_argument('address', help='The address to attempt to ping.')
+
+ parser.add_argument('-t', '--timeout', help='The maximum amount of time to\
+ wait until ping timeout.', type=int, default=3000)
+
+ parser.add_argument('-c', '--request_count', help='The number of attempts \
+ to make. See --infinite to attempt requests until \
+ stopped.', type=int, default=3)
+
+ parser.add_argument('-i', '--infinite', help='Flag to continuously ping \
+ a host until stopped.', action='store_true')
+
+ parser.add_argument('-I', '--ipv6', action='store_true', help='Flag to \
+ use IPv6.')
+
+ parser.add_argument('-s', '--packet_size', type=int, help='Designate the\
+ amount of data to send per packet.', default=64)
+
+ parser.add_argument('-T', '--test_case', action='store_true', help='Flag \
+ to run the default test case suite.')
+
+ parser.add_argument('-S', '--source_address', help='Source address from which \
+ ICMP Echo packets will be sent.')
+
+ parsed = parser.parse_args()
+
+ if parsed.infinite:
+ sys.exit(list(verbose_ping(parsed.address, parsed.timeout,
+ None, parsed.packet_size,
+ ipv6=parsed.ipv6,
+ sourceIP=parsed.source_address))[:-1])
+
+ else:
+ sys.exit(list(verbose_ping(parsed.address, parsed.timeout,
+ parsed.request_count, parsed.packet_size,
+ ipv6=parsed.ipv6,
+ sourceIP=parsed.source_address))[:-1])
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..02cc4eb
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python
+# coding: utf-8
+
+"""
+ distutils setup
+ ~~~~~~~~~~~~~~~
+
+ :homepage: https://github.com/l4m3rx/python-ping/
+ :copyleft: 1989-2016 by the python-ping team, see AUTHORS for more details.
+ :license: GNU GPL v2, see LICENSE for more details.
+"""
+
+import os
+import subprocess
+import sys
+import time
+import warnings
+
+from setuptools import setup, find_packages, Command
+
+PACKAGE_ROOT = os.path.dirname(os.path.abspath(__file__))
+
+
+#VERBOSE = True
+VERBOSE = False
+
+def _error(msg):
+ if VERBOSE:
+ warnings.warn(msg)
+ return ""
+
+def get_version_from_git():
+ try:
+ process = subprocess.Popen(
+ # %ct: committer date, UNIX timestamp
+ ["/usr/bin/git", "log", "--pretty=format:%ct-%h", "-1", "HEAD"],
+ shell=False, cwd=PACKAGE_ROOT,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ )
+ except Exception as err:
+ return _error("Can't get git hash: %s" % err)
+
+ process.wait()
+ returncode = process.returncode
+ if returncode != 0:
+ return _error(
+ "Can't get git hash, returncode was: %r"
+ " - git stdout: %r"
+ " - git stderr: %r"
+ % (returncode, process.stdout.readline(), process.stderr.readline())
+ )
+
+ output = process.stdout.readline().strip()
+ try:
+ raw_timestamp, mhash = output.split("-", 1)
+ timestamp = int(raw_timestamp)
+ except Exception as err:
+ return _error("Error in git log output! Output was: %r" % output)
+
+ try:
+ timestamp_formatted = time.strftime("%Y.%m.%d", time.gmtime(timestamp))
+ except Exception as err:
+ return _error("can't convert %r to time string: %s" % (timestamp, err))
+
+ return "%s.%s" % (timestamp_formatted, mhash)
+
+
+# convert creole to ReSt on-the-fly, see also:
+# https://code.google.com/p/python-creole/wiki/UseInSetup
+try:
+ from creole.setup_utils import get_long_description
+except ImportError:
+ if "register" in sys.argv or "sdist" in sys.argv or "--long-description" in sys.argv:
+ etype, evalue, etb = sys.exc_info()
+ evalue = etype("%s - Please install python-creole >= v0.8 - e.g.: pip install python-creole" % evalue)
+ raise etype(evalue).with_traceback(etb)
+ long_description = None
+else:
+ long_description = get_long_description(PACKAGE_ROOT)
+
+
+def get_authors():
+ authors = []
+ try:
+ f = file(os.path.join(PACKAGE_ROOT, "AUTHORS"), "r")
+ for line in f:
+ if not line.strip().startswith("*"):
+ continue
+ if "--" in line:
+ line = line.split("--", 1)[0]
+ authors.append(line.strip(" *\r\n"))
+ f.close()
+ authors.sort()
+ except Exception as err:
+ authors = "[Error: %s]" % err
+ return authors
+
+
+setup(
+ name='python-ping',
+# version=get_version_from_git(),
+ version='25102016',
+ description='A pure python ICMP ping implementation using raw sockets.',
+ long_description=long_description,
+ author=get_authors(),
+ maintainer="Georgi Kolev",
+ maintainer_email="georgi.kolev@gmail.com",
+ url='https://github.com/l4m3rx/python-ping/',
+ keywords="ping icmp network latency",
+ packages=find_packages(),
+ include_package_data=True, # include package data under svn source control
+ zip_safe=False,
+ scripts=["ping.py"],
+ classifiers=[
+ # http://pypi.python.org/pypi?%3Aaction=list_classifiers
+# "Development Status :: 4 - Beta",
+ "Development Status :: 5 - Production/Stable",
+ "Environment :: Web Environment",
+ "Intended Audience :: Developers",
+ "Intended Audience :: Education",
+ "Intended Audience :: End Users/Desktop",
+ "License :: OSI Approved :: GNU General Public License (GPL)",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python",
+ "Topic :: Internet",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+ "Topic :: System :: Networking :: Monitoring",
+ ],
+ test_suite="tests",
+)