diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0682048..403267c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,18 +1,28 @@ +ci: + autoupdate_schedule: monthly repos: + - repo: https://github.com/adamchainz/django-upgrade + rev: 1.13.0 + hooks: + - id: django-upgrade + args: [--target-version, '4.2'] - repo: https://github.com/asottile/pyupgrade - rev: v2.31.1 + rev: v3.3.1 hooks: - id: pyupgrade args: ["--py37-plus"] - - repo: https://github.com/asottile/reorder_python_imports - rev: v3.0.1 + - repo: https://github.com/PyCQA/isort + rev: 5.12.0 hooks: - - id: reorder-python-imports - additional_dependencies: ["setuptools>60.9"] + - id: isort - repo: https://github.com/psf/black rev: 22.3.0 hooks: - id: black + - repo: https://github.com/asottile/blacken-docs + rev: 1.13.0 + hooks: + - id: blacken-docs - repo: https://github.com/PyCQA/flake8 rev: 4.0.1 hooks: @@ -20,7 +30,7 @@ repos: additional_dependencies: - flake8-bugbear - flake8-implicit-str-concat - args: ["--max-line-length=100"] + args: ["--max-line-length=125"] - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.1.0 hooks: @@ -31,4 +41,3 @@ repos: rev: v2.5.6 hooks: - id: pip-audit - args: ["--ignore-vuln", "PYSEC-2022-203"] diff --git a/CHANGELOG.md b/CHANGELOG.md index 413ca15..2874681 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ ## Release History +### 0.11.2 (2023-05-23) + +* improved typing of various functions; +* updated dependencies. + + ### 0.11.1 (2022-11-20) * Fixed a bug in the command line when no sub-command is specified. diff --git a/bin/lsb.py b/bin/lsb.py index 122bc26..be87a79 100755 --- a/bin/lsb.py +++ b/bin/lsb.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- - # Stegano - Stegano is a pure Python steganography module. -# Copyright (C) 2010-2022 Cédric Bonhomme - https://www.cedricbonhomme.org +# Copyright (C) 2010-2023 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://github.com/cedricbonhomme/Stegano # @@ -26,6 +24,7 @@ __revision__ = "$Date: 2019/06/04 $" __license__ = "GPLv3" import inspect + import crayons try: @@ -34,10 +33,10 @@ try: except Exception: print("Install stegano: pipx install Stegano") -from stegano import tools - import argparse +from stegano import tools + class ValidateGenerator(argparse.Action): def __call__(self, parser, args, values, option_string=None): @@ -76,7 +75,7 @@ def main(): choices=tools.ENCODINGS.keys(), default="UTF-8", help="Specify the encoding of the message to hide." - + " UTF-8 (default) or UTF-32LE.", + " UTF-8 (default) or UTF-32LE.", ) # Generator @@ -131,7 +130,7 @@ def main(): choices=tools.ENCODINGS.keys(), default="UTF-8", help="Specify the encoding of the message to reveal." - + " UTF-8 (default) or UTF-32LE.", + " UTF-8 (default) or UTF-32LE.", ) # Generator @@ -179,7 +178,7 @@ def main(): generator = getattr(generators, arguments.generator_function[0])() except AttributeError: - print("Unknown generator: {}".format(arguments.generator_function)) + print(f"Unknown generator: {arguments.generator_function}") exit(1) if arguments.command == "hide": @@ -223,6 +222,6 @@ def main(): all_generators = inspect.getmembers(generators, inspect.isfunction) for generator in all_generators: print("Generator id:") - print(" {}".format(crayons.green(generator[0], bold=True))) + print(f" {crayons.green(generator[0], bold=True)}") print("Desciption:") - print(" {}".format(generator[1].__doc__)) + print(f" {generator[1].__doc__}") diff --git a/bin/parity.py b/bin/parity.py index c80bf82..e248d5f 100644 --- a/bin/parity.py +++ b/bin/parity.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- - # Stegano - Stegano is a pure Python steganography module. -# Copyright (C) 2010-2022 Cédric Bonhomme - https://www.cedricbonhomme.org +# Copyright (C) 2010-2023 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://github.com/cedricbonhomme/Stegano # @@ -24,10 +22,10 @@ __version__ = "$Revision: 0.7 $" __date__ = "$Date: 2016/08/25 $" __license__ = "GPLv3" -from PIL import Image - import argparse +from PIL import Image + try: from stegano.steganalysis import parity except Exception: diff --git a/bin/red.py b/bin/red.py index 51230ca..216d27d 100644 --- a/bin/red.py +++ b/bin/red.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- - # Stegano - Stegano is a pure Python steganography module. -# Copyright (C) 2010-2022 Cédric Bonhomme - https://www.cedricbonhomme.org +# Copyright (C) 2010-2023 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://github.com/cedricbonhomme/Stegano # diff --git a/bin/statistics.py b/bin/statistics.py index b2f80a9..6184266 100644 --- a/bin/statistics.py +++ b/bin/statistics.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- - # Stegano - Stegano is a pure Python steganography module. -# Copyright (C) 2010-2022 Cédric Bonhomme - https://www.cedricbonhomme.org +# Copyright (C) 2010-2023 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://github.com/cedricbonhomme/Stegano # @@ -25,10 +23,10 @@ __date__ = "$Date: 2016/08/26 $" __revision__ = "$Date: 2016/08/26 $" __license__ = "GPLv3" -from PIL import Image - import argparse +from PIL import Image + try: from stegano.steganalysis import statistics except Exception: diff --git a/docs/conf.py b/docs/conf.py index 25a0a52..4d7973e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Stéganô documentation build configuration file, created by # sphinx-quickstart on Wed Jul 25 13:33:39 2012. @@ -10,17 +9,13 @@ # # All configuration values have a default; values that are commented out # serve to show the default. - # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) - # -- General configuration ----------------------------------------------------- - # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' - # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [] @@ -39,7 +34,7 @@ master_doc = "index" # General information about the project. project = "Stegano" -copyright = "2010-2022, Cédric Bonhomme" +copyright = "2010-2023, Cédric Bonhomme" author = "Cédric Bonhomme " # The version info for the project you're documenting, acts as replacement for @@ -170,6 +165,6 @@ latex_documents = [ latex_show_urls = True latex_show_pagerefs = True -ADDITIONAL_PREAMBLE = """ +ADDITIONAL_PREAMBLE = r""" \setcounter{tocdepth}{3} """ diff --git a/pyproject.toml b/pyproject.toml index 10a71fe..a4da57b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "stegano" -version = "0.11.1" +version = "0.11.2" description = "A pure Python Steganography module." authors = [ "Cédric Bonhomme " @@ -76,3 +76,6 @@ show_error_context = true pretty = true exclude = "build|dist|docs|stegano.egg-info" + +[tool.isort] +profile = "black" diff --git a/stegano/exifHeader/exifHeader.py b/stegano/exifHeader/exifHeader.py index 9958b96..e90e7ca 100644 --- a/stegano/exifHeader/exifHeader.py +++ b/stegano/exifHeader/exifHeader.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- - # Stegano - Stegano is a pure Python steganography module. -# Copyright (C) 2010-2022 Cédric Bonhomme - https://www.cedricbonhomme.org +# Copyright (C) 2010-2023 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://git.sr.ht/~cedric/stegano # @@ -26,6 +24,7 @@ __revision__ = "$Date: 2017/01/18 $" __license__ = "GPLv3" import piexif + from stegano import tools @@ -37,11 +36,11 @@ def hide( img_format=None, ): """Hide a message (string) in an image.""" - from zlib import compress from base64 import b64encode + from zlib import compress if secret_file is not None: - with open(secret_file, "r") as f: + with open(secret_file) as f: secret_message = f.read() try: diff --git a/stegano/lsb/generators.py b/stegano/lsb/generators.py index 969e3ec..90685ca 100644 --- a/stegano/lsb/generators.py +++ b/stegano/lsb/generators.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # Stegano - Stegano is a pure Python steganography module. -# Copyright (C) 2010-2022 Cédric Bonhomme - https://www.cedricbonhomme.org +# Copyright (C) 2010-2023 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://git.sr.ht/~cedric/stegano # @@ -24,10 +24,11 @@ __revision__ = "$Date: 2021/11/29 $" __license__ = "GPLv3" import itertools +import math +from typing import Any, Dict, Iterator, List + import cv2 import numpy as np -import math -from typing import Dict, Iterator, List, Any def identity() -> Iterator[int]: diff --git a/stegano/lsb/lsb.py b/stegano/lsb/lsb.py index 9dc776f..7b762de 100644 --- a/stegano/lsb/lsb.py +++ b/stegano/lsb/lsb.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- - # Stegano - Stegano is a pure Python steganography module. -# Copyright (C) 2010-2022 Cédric Bonhomme - https://www.cedricbonhomme.org +# Copyright (C) 2010-2023 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://git.sr.ht/~cedric/stegano # @@ -27,9 +25,10 @@ __license__ = "GPLv3" from typing import IO, Iterator, Union -from .generators import identity from stegano import tools +from .generators import identity + def hide( image: Union[str, IO[bytes]], diff --git a/stegano/red/red.py b/stegano/red/red.py index bea8aac..0704e23 100755 --- a/stegano/red/red.py +++ b/stegano/red/red.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- - # Stegano - Stéganô is a basic Python Steganography module. -# Copyright (C) 2010-2022 Cédric Bonhomme - https://www.cedricbonhomme.org +# Copyright (C) 2010-2023 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://git.sr.ht/~cedric/stegano # diff --git a/stegano/steganalysis/parity.py b/stegano/steganalysis/parity.py index 9f08c75..a245ef0 100644 --- a/stegano/steganalysis/parity.py +++ b/stegano/steganalysis/parity.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- - # Stegano - Stegano is a pure Python steganography module. -# Copyright (C) 2010-2022 Cédric Bonhomme - https://www.cedricbonhomme.org +# Copyright (C) 2010-2023 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://git.sr.ht/~cedric/stegano # diff --git a/stegano/steganalysis/statistics.py b/stegano/steganalysis/statistics.py index 310769f..edd423c 100644 --- a/stegano/steganalysis/statistics.py +++ b/stegano/steganalysis/statistics.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # Stegano - Stegano is a pure Python steganography module. -# Copyright (C) 2010-2022 Cédric Bonhomme - https://www.cedricbonhomme.org +# Copyright (C) 2010-2023 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://git.sr.ht/~cedric/stegano # @@ -24,9 +24,7 @@ __revision__ = "$Date: 2021/11/01 $" __license__ = "GPLv3" import typing - -from collections import Counter -from collections import OrderedDict +from collections import Counter, OrderedDict def steganalyse(img): diff --git a/stegano/tools.py b/stegano/tools.py index d33b131..156b40a 100755 --- a/stegano/tools.py +++ b/stegano/tools.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # Stegano - Stegano is a pure Python steganography module. -# Copyright (C) 2010-2022 Cédric Bonhomme - https://www.cedricbonhomme.org +# Copyright (C) 2010-2023 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://git.sr.ht/~cedric/stegano # diff --git a/tests/test_exifHeader.py b/tests/test_exifHeader.py index 5880f93..eef0e02 100644 --- a/tests/test_exifHeader.py +++ b/tests/test_exifHeader.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- - # Stegano - Stegano is a pure Python steganography module. -# Copyright (C) 2010-2022 Cédric Bonhomme - https://www.cedricbonhomme.org +# Copyright (C) 2010-2023 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://git.sr.ht/~cedric/stegano # @@ -25,9 +23,9 @@ __date__ = "$Date: 2016/05/17 $" __revision__ = "$Date: 2017/01/18 $" __license__ = "GPLv3" +import io import os import unittest -import io from stegano import exifHeader diff --git a/tests/test_generators.py b/tests/test_generators.py index 29cb113..66dbc1b 100644 --- a/tests/test_generators.py +++ b/tests/test_generators.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- - # Stegano - Stegano is a pure Python steganography module. -# Copyright (C) 2010-2022 Cédric Bonhomme - https://www.cedricbonhomme.org +# Copyright (C) 2010-2023 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://git.sr.ht/~cedric/stegano # @@ -25,8 +23,9 @@ __date__ = "$Date: 2017/03/01 $" __revision__ = "$Date: 2017/03/01 $" __license__ = "GPLv3" -import unittest import itertools +import unittest + import cv2 import numpy as np @@ -71,7 +70,7 @@ class TestGenerators(unittest.TestCase): def test_eratosthenes(self): """Test the Eratosthenes sieve.""" - with open("./tests/expected-results/eratosthenes", "r") as f: + with open("./tests/expected-results/eratosthenes") as f: self.assertEqual( tuple(itertools.islice(generators.eratosthenes(), 168)), tuple(int(line) for line in f), @@ -79,7 +78,7 @@ class TestGenerators(unittest.TestCase): def test_composite(self): """Test the composite sieve.""" - with open("./tests/expected-results/composite", "r") as f: + with open("./tests/expected-results/composite") as f: self.assertEqual( tuple(itertools.islice(generators.composite(), 114)), tuple(int(line) for line in f), @@ -87,7 +86,7 @@ class TestGenerators(unittest.TestCase): def test_fermat(self): """Test the Fermat generator.""" - with open("./tests/expected-results/fermat", "r") as f: + with open("./tests/expected-results/fermat") as f: self.assertEqual( tuple(itertools.islice(generators.fermat(), 9)), tuple(int(line) for line in f), @@ -95,7 +94,7 @@ class TestGenerators(unittest.TestCase): def test_triangular_numbers(self): """Test the Triangular numbers generator.""" - with open("./tests/expected-results/triangular_numbers", "r") as f: + with open("./tests/expected-results/triangular_numbers") as f: self.assertEqual( tuple(itertools.islice(generators.triangular_numbers(), 54)), tuple(int(line) for line in f), @@ -103,7 +102,7 @@ class TestGenerators(unittest.TestCase): def test_mersenne(self): """Test the Mersenne generator.""" - with open("./tests/expected-results/mersenne", "r") as f: + with open("./tests/expected-results/mersenne") as f: self.assertEqual( tuple(itertools.islice(generators.mersenne(), 20)), tuple(int(line) for line in f), @@ -111,7 +110,7 @@ class TestGenerators(unittest.TestCase): def test_carmichael(self): """Test the Carmichael generator.""" - with open("./tests/expected-results/carmichael", "r") as f: + with open("./tests/expected-results/carmichael") as f: self.assertEqual( tuple(itertools.islice(generators.carmichael(), 33)), tuple(int(line) for line in f), @@ -119,7 +118,7 @@ class TestGenerators(unittest.TestCase): def test_ackermann_slow(self): """Test the Ackermann set.""" - with open("./tests/expected-results/ackermann", "r") as f: + with open("./tests/expected-results/ackermann") as f: self.assertEqual(generators.ackermann_slow(3, 1), int(f.readline())) self.assertEqual(generators.ackermann_slow(3, 2), int(f.readline())) @@ -127,13 +126,13 @@ class TestGenerators(unittest.TestCase): """Test the Naive Ackermann generator""" gen = generators.ackermann_naive(3) next(gen) - with open("./tests/expected-results/ackermann", "r") as f: + with open("./tests/expected-results/ackermann") as f: self.assertEqual(next(gen), int(f.readline())) self.assertEqual(next(gen), int(f.readline())) def test_ackermann_fast(self): """Test the Ackermann set.""" - with open("./tests/expected-results/ackermann", "r") as f: + with open("./tests/expected-results/ackermann") as f: self.assertEqual(generators.ackermann_fast(3, 1), int(f.readline())) self.assertEqual(generators.ackermann_fast(3, 2), int(f.readline())) self.assertEqual(generators.ackermann_fast(4, 1), int(f.readline())) @@ -142,13 +141,13 @@ class TestGenerators(unittest.TestCase): """Test the Ackermann generator""" gen = generators.ackermann(3) next(gen) - with open("./tests/expected-results/ackermann", "r") as f: + with open("./tests/expected-results/ackermann") as f: self.assertEqual(next(gen), int(f.readline())) self.assertEqual(next(gen), int(f.readline())) def test_LFSR(self): """Test the LFSR generator""" - with open("./tests/expected-results/LFSR", "r") as f: + with open("./tests/expected-results/LFSR") as f: self.assertEqual( tuple(itertools.islice(generators.LFSR(2**8), 256)), tuple(int(line) for line in f), diff --git a/tests/test_lsb.py b/tests/test_lsb.py index 9355b7c..836f43c 100644 --- a/tests/test_lsb.py +++ b/tests/test_lsb.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- - # Stegano - Stegano is a pure Python steganography module. -# Copyright (C) 2010-2022 Cédric Bonhomme - https://www.cedricbonhomme.org +# Copyright (C) 2010-2023 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://git.sr.ht/~cedric/stegano # @@ -173,7 +171,12 @@ class TestLSB(unittest.TestCase): @patch("builtins.input", return_value="n") def test_refuse_convert_rgb(self, input): message_to_hide = "Hello World!" - with self.assertRaises(Exception): + # lsb.hide( + # "./tests/sample-files/Lenna-grayscale.png", + # message_to_hide, + # generators.eratosthenes(), + # ) + with self.assertRaisesRegex(Exception, "Not a RGB image."): lsb.hide( "./tests/sample-files/Lenna-grayscale.png", message_to_hide, @@ -210,7 +213,9 @@ class TestLSB(unittest.TestCase): with open("./tests/sample-files/lorem_ipsum.txt") as f: message = f.read() message += message * 2 - with self.assertRaises(Exception): + with self.assertRaisesRegex( + Exception, "The message you want to hide is too long:" + ): lsb.hide("./tests/sample-files/Lenna.png", message, generators.identity()) def test_hide_and_reveal_with_bad_generator(self): diff --git a/tests/test_red.py b/tests/test_red.py index 3171fa4..6bf6607 100644 --- a/tests/test_red.py +++ b/tests/test_red.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- - # Stegano - Stegano is a pure Python steganography module. -# Copyright (C) 2010-2022 Cédric Bonhomme - https://www.cedricbonhomme.org +# Copyright (C) 2010-2023 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://git.sr.ht/~cedric/stegano # diff --git a/tests/test_tools.py b/tests/test_tools.py index 4899246..1a6926d 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- - # Stegano - Stegano is a pure Python steganography module. -# Copyright (C) 2010-2022 Cédric Bonhomme - https://www.cedricbonhomme.org +# Copyright (C) 2010-2023 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://git.sr.ht/~cedric/stegano # @@ -83,7 +81,7 @@ class TestTools(unittest.TestCase): self.assertEqual(list(result), [(1, 2), (3, 4), (5, "X")]) def test_binary2base64(self): - with open("./tests/expected-results/binary2base64", "r") as f: + with open("./tests/expected-results/binary2base64") as f: expected_value = f.read() value = tools.binary2base64("tests/sample-files/free-software-song.ogg") self.assertEqual(expected_value, value)