Browse Source

arg `--free-bytes`: support human-readable formats like '2TB' or '2GiB'

Fabian Peter Hammerle 5 years ago
parent
commit
1ee810877f
7 changed files with 123 additions and 5 deletions
  1. 1 1
      .travis.yml
  2. 1 0
      Pipfile
  3. 45 1
      Pipfile.lock
  4. 3 2
      README.md
  5. 34 1
      free_disk.py
  6. 1 0
      setup.py
  7. 38 0
      tests/data_size_to_bytes_test.py

+ 1 - 1
.travis.yml

@@ -16,4 +16,4 @@ install:
 
 script:
 - pipenv run pylint free_disk
-# - pipenv run pytest
+- pipenv run pytest

+ 1 - 0
Pipfile

@@ -8,6 +8,7 @@ free-disk = {editable = true, path = "."}
 
 [dev-packages]
 pylint = ">=2.3.0"
+pytest = "*"
 
 [requires]
 python_version = "3"

+ 45 - 1
Pipfile.lock

@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "ec9dc24d5bd5efbbd88d160e99130ffce19ea5a2ae78a0488424118faceb91f9"
+            "sha256": "cce3f2f618132127134ad38ab756fd1fba8e7b934e9d72f684b51a3786f8a90e"
         },
         "pipfile-spec": 6,
         "requires": {
@@ -29,6 +29,20 @@
             ],
             "version": "==2.2.5"
         },
+        "atomicwrites": {
+            "hashes": [
+                "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4",
+                "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"
+            ],
+            "version": "==1.3.0"
+        },
+        "attrs": {
+            "hashes": [
+                "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79",
+                "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"
+            ],
+            "version": "==19.1.0"
+        },
         "isort": {
             "hashes": [
                 "sha256:01cb7e1ca5e6c5b3f235f0385057f70558b70d2f00320208825fa62887292f43",
@@ -77,6 +91,28 @@
             ],
             "version": "==0.6.1"
         },
+        "more-itertools": {
+            "hashes": [
+                "sha256:2112d2ca570bb7c3e53ea1a35cd5df42bb0fd10c45f0fb97178679c3c03d64c7",
+                "sha256:c3e4748ba1aad8dba30a4886b0b1a2004f9a863837b8654e7059eebf727afa5a"
+            ],
+            "markers": "python_version > '2.7'",
+            "version": "==7.0.0"
+        },
+        "pluggy": {
+            "hashes": [
+                "sha256:19ecf9ce9db2fce065a7a0586e07cfb4ac8614fe96edf628a264b1c70116cf8f",
+                "sha256:84d306a647cc805219916e62aab89caa97a33a1dd8c342e87a37f91073cd4746"
+            ],
+            "version": "==0.9.0"
+        },
+        "py": {
+            "hashes": [
+                "sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa",
+                "sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"
+            ],
+            "version": "==1.8.0"
+        },
         "pylint": {
             "hashes": [
                 "sha256:5d77031694a5fb97ea95e828c8d10fc770a1df6eb3906067aaed42201a8a6a09",
@@ -85,6 +121,14 @@
             "index": "pypi",
             "version": "==2.3.1"
         },
+        "pytest": {
+            "hashes": [
+                "sha256:3773f4c235918987d51daf1db66d51c99fac654c81d6f2f709a046ab446d5e5d",
+                "sha256:b7802283b70ca24d7119b32915efa7c409982f59913c1a6c0640aacf118b95f5"
+            ],
+            "index": "pypi",
+            "version": "==4.4.1"
+        },
         "six": {
             "hashes": [
                 "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",

+ 3 - 2
README.md

@@ -5,7 +5,7 @@
 ## Install
 
 ```sh
-pip3 install --user free-disk
+pip3 install --user --upgrade free-disk
 ```
 
 ## Usage
@@ -15,11 +15,12 @@ free-disk --help
 free-disk --debug --free-bytes 1024 /dir/to/cleanup
 ```
 
-## Lint
+## Tests
 
 ```sh
 pip3 install --user pipenv
 git clone https://github.com/fphammerle/free-disk.git
 cd freesurfer-volume-reader
 pipenv run pylint free_disk
+pipenv run pytest
 ```

+ 34 - 1
free_disk.py

@@ -3,6 +3,37 @@ import datetime
 import logging
 import os
 import shutil
+import re
+
+# https://en.wikipedia.org/wiki/Template:Quantities_of_bytes
+DATA_SIZE_UNIT_BYTE_CONVERSION_FACTOR = {
+    'B': 1,
+    'kB': 10**3,
+    'KB': 10**3,
+    'MB': 10**6,
+    'GB': 10**9,
+    'TB': 10**12,
+    'KiB': 2**10,
+    'MiB': 2**20,
+    'GiB': 2**30,
+    'TiB': 2**40,
+}
+
+
+def data_size_to_bytes(size_with_unit: str) -> int:
+    match = re.match(r'^([\d\.]+)\s*([A-Za-z]+)?$', size_with_unit)
+    if not match:
+        raise ValueError('Unable to parse data size {!r}'.format(size_with_unit))
+    unit_symbol = match.group(2)
+    if unit_symbol:
+        try:
+            byte_conversion_factor = DATA_SIZE_UNIT_BYTE_CONVERSION_FACTOR[unit_symbol]
+        except KeyError:
+            raise ValueError('Unknown data size unit symbol {!r}'.format(unit_symbol))
+    else:
+        byte_conversion_factor = 1
+    byte_size = float(match.group(1)) * byte_conversion_factor
+    return int(round(byte_size, 0))
 
 
 def main():
@@ -10,12 +41,14 @@ def main():
         description='Delete files with earliest modification date'
                     ' until a minimum of --free-bytes are available on the respective disk')
     argparser.add_argument('-d', '--debug', action='store_true')
-    argparser.add_argument('--free-bytes', type=int, required=True)
+    argparser.add_argument('--free-bytes', type=data_size_to_bytes, required=True,
+                           help='examples: 1024, 1024B, 4KiB, 4KB, 2TB')
     argparser.add_argument('root_dir_path', metavar='ROOT_DIR')
     args = argparser.parse_args()
     logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO,
                         format='%(asctime)s:%(levelname)s:%(message)s',
                         datefmt='%Y-%m-%dT%H:%M:%S%z')
+    logging.debug('Required free bytes: %d', args.free_bytes)
     disk_usage = shutil.disk_usage(args.root_dir_path)
     logging.debug(disk_usage)
     if disk_usage.free >= args.free_bytes:

+ 1 - 0
setup.py

@@ -36,5 +36,6 @@ setuptools.setup(
     ],
     tests_require=[
         'pylint>=2.3.0',
+        'pytest',
     ],
 )

+ 38 - 0
tests/data_size_to_bytes_test.py

@@ -0,0 +1,38 @@
+import pytest
+
+import free_disk
+
+
+@pytest.mark.parametrize(('data_size_with_unit', 'expected_bytes'), [
+    ('123', 123),
+    ('123B', 123),
+    ('123.0B', 123),
+    ('1kB', 1000),
+    ('2kB', 2000),
+    ('2.5kB', 2500),
+    ('2KB', 2000),
+    ('8MB', 8 * (10**6)),
+    ('8.5MB', 8.5 * (10**6)),
+    ('32GB', 32 * (10**9)),
+    ('9TB', 9 * (10**12)),
+    ('3KiB', 3 * (1024**1)),
+    ('40MiB', 40 * (1024**2)),
+    ('512GiB', 512 * (1024**3)),
+    ('7TiB', 7 * (1024**4)),
+    ('123 B', 123),
+    ('123\tB', 123),
+    ('123.0  B', 123),
+    ('1  kB', 1000),
+    ('1  MiB', 1024**2),
+])
+def test_data_size_to_bytes(data_size_with_unit, expected_bytes):
+    assert expected_bytes == free_disk.data_size_to_bytes(data_size_with_unit)
+
+
+@pytest.mark.parametrize('data_size_with_unit', [
+    'abcdef',
+    '123G',
+])
+def test_data_size_to_bytes_fail(data_size_with_unit):
+    with pytest.raises(ValueError):
+        free_disk.data_size_to_bytes(data_size_with_unit)