-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
51 changed files
with
13,696 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# This workflow will install Python dependencies, run tests and lint with a single version of Python | ||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions | ||
|
||
name: Python application | ||
|
||
on: | ||
push: | ||
branches: [ master ] | ||
pull_request: | ||
branches: [ master ] | ||
|
||
jobs: | ||
build: | ||
|
||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v2 | ||
- name: Set up Python 3.9 | ||
uses: actions/setup-python@v2 | ||
with: | ||
python-version: 3.9 | ||
- name: Install dependencies | ||
run: | | ||
python -m pip install --upgrade pip | ||
pip install flake8 | ||
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi | ||
- name: Lint with flake8 | ||
run: | | ||
# stop the build if there are Python syntax errors or undefined names | ||
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics | ||
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide | ||
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics | ||
- name: Test with unittest module | ||
run: | | ||
python -m unittest discover tests -v |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# source-code-size-counter | ||
This repository contains a simple tool used for calculating total size (both kB and lines of code) | ||
of program's code. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import os | ||
from argparse import ArgumentParser | ||
|
||
from src.code_size_counter import CodeSizeCounter | ||
|
||
|
||
def format_to_kilobytes(total_bytes): | ||
""" | ||
convert the given number of bytes to kilobytes and round to 2 decimal digits | ||
:param total_bytes: number of bytes to convert and format | ||
:return: converted kilobytes rounded to 2 decimal digits | ||
""" | ||
kilobytes = total_bytes / 1024 | ||
return round(kilobytes, 2) | ||
|
||
|
||
def format_extensions(file_extensions): | ||
""" | ||
format set of file extensions for printing | ||
:param file_extensions: set of file extensions | ||
""" | ||
dot_prefixed_extensions = map(lambda e: f'.{e}', file_extensions) | ||
return ", ".join(dot_prefixed_extensions) | ||
|
||
|
||
def config_args(): | ||
""" | ||
configure the command line arguments of the program | ||
:return: parsed script arguments | ||
""" | ||
parser = ArgumentParser(description='Calculate the total size (both kB and lines of code) of program\'s code.') | ||
parser.add_argument('-d', '--directory', type=str, required=True) | ||
parser.add_argument('-e', '--extension', nargs='+', required=True, default=[]) | ||
parser.add_argument('-l', '--log', default=False, action='store_true') | ||
parser.add_argument('-x', '--exclude', nargs='+', default=[]) | ||
return parser.parse_args() | ||
|
||
|
||
def main(): | ||
args = config_args() | ||
file_extensions = tuple(args.extension) | ||
excluded_items = tuple(map(lambda ex: os.path.join(args.directory, ex), args.exclude)) | ||
|
||
code_size_counter = CodeSizeCounter(args.directory, file_extensions, args.log, excluded_items) | ||
code_size = code_size_counter.calculate_size() | ||
|
||
print('=' * 60) | ||
print(f'Total {format_extensions(file_extensions)} files: {code_size.total_files}') | ||
print(f'Total size of {format_extensions(file_extensions)} files: {format_to_kilobytes(code_size.total_size)} kB') | ||
print(f'Total lines of code: {code_size.total_lines}') | ||
print('=' * 60) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import os | ||
|
||
from src.file_tools import FileSetSize, FileManager, get_path_with_slashes | ||
|
||
|
||
class CodeSizeCounter: | ||
""" | ||
class responsible for computing the source code size in the given directory | ||
""" | ||
|
||
def __init__(self, directory, file_extensions, print_logs, excluded_items): | ||
""" | ||
:param directory: the directory where to search files | ||
:param file_extensions: extension of the files that we're searching | ||
:param print_logs: should the program print its progress? (e.g. 'file XXX processed) | ||
:param excluded_items: absolute path to directories & files to exclude | ||
""" | ||
self._directory = directory | ||
self._file_extensions = file_extensions | ||
self._print_logs = print_logs | ||
self._excluded_items = excluded_items | ||
|
||
def calculate_size(self): | ||
""" | ||
count lines, size (in bytes) and number of files in the directory with the selected file extension | ||
""" | ||
return self._calculate_size(self._directory) | ||
|
||
def _calculate_size(self, directory): | ||
""" | ||
count lines, size (in bytes) and number of files in the directory with the selected file extension | ||
:param directory: directory where to search files | ||
""" | ||
|
||
# if it's in excluded files/directories, return | ||
if self._is_excluded(directory): | ||
return FileSetSize.empty() | ||
|
||
items = os.listdir(directory) | ||
files = [f for f in items if os.path.isfile(os.path.join(directory, f))] | ||
directories = [d for d in items if os.path.isdir(os.path.join(directory, d))] | ||
|
||
file_set_size = FileSetSize.empty() | ||
|
||
for sub_dir in directories: # add the size of all subdirs | ||
directory_path = os.path.join(directory, sub_dir) | ||
directory_size = self._calculate_size(directory_path) | ||
file_set_size.add(directory_size) | ||
|
||
for file in files: # add the size of all files | ||
file_path = os.path.join(directory, file) | ||
file_manager = FileManager(file_path) | ||
if (not file_manager.has_one_of_extensions(self._file_extensions)) or self._is_excluded(file_path): | ||
continue | ||
|
||
file_size = FileSetSize(file_manager.get_size(), file_manager.get_lines_count(), 1) | ||
file_set_size.add(file_size) | ||
|
||
if self._print_logs: | ||
print(f'{get_path_with_slashes(file_path)} processed') | ||
|
||
return file_set_size | ||
|
||
def _is_excluded(self, path): | ||
""" | ||
check if the directory/file is excluded from the code size calculation | ||
:param path: path of the directory/file to check | ||
""" | ||
return any(os.path.samefile(path, ex) for ex in self._excluded_items) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import os | ||
|
||
|
||
class FileSetSize: | ||
""" | ||
class representing | ||
- total size | ||
- total lines count | ||
- number of files | ||
of a set of files | ||
""" | ||
|
||
def __init__(self, total_size, total_lines, total_files): | ||
""" | ||
:param total_size: total size of all files in the set (in bytes) | ||
:param total_lines: total lines of code of all files in the set | ||
:param total_files: number of files in the set | ||
""" | ||
self.total_size = total_size | ||
self.total_lines = total_lines | ||
self.total_files = total_files | ||
|
||
def add(self, file_set_size): | ||
""" | ||
add file_set_info to the current object | ||
e.g. sum the corresponding fields | ||
:param file_set_size: | ||
""" | ||
self.total_files += file_set_size.total_files | ||
self.total_lines += file_set_size.total_lines | ||
self.total_size += file_set_size.total_size | ||
|
||
@staticmethod | ||
def empty(): | ||
""" | ||
create empty instance of class FileSetSizeInfo | ||
:return: | ||
""" | ||
return FileSetSize(0, 0, 0) | ||
|
||
|
||
class FileManager: | ||
""" | ||
Helper class for file management | ||
""" | ||
|
||
def __init__(self, file_path): | ||
self.file_path = file_path | ||
|
||
def get_size(self): | ||
""" | ||
get size of the file in bytes | ||
""" | ||
return os.path.getsize(self.file_path) | ||
|
||
def get_lines_count(self): | ||
""" | ||
get number of lines in the file | ||
""" | ||
with open(self.file_path, 'r') as file: | ||
return sum(1 for _ in file) | ||
|
||
def has_one_of_extensions(self, extensions): | ||
""" | ||
check if the file has one of the extensions | ||
:param extensions: collection of expected extensions | ||
""" | ||
return any(self._has_extension(e) for e in extensions) | ||
|
||
def _has_extension(self, extension): | ||
""" | ||
check if the file has given file extension (e.g. '.py') | ||
:param extension: given file extension (e.g. '.py') | ||
""" | ||
return self.file_path.endswith(f'.{extension}') | ||
|
||
|
||
def get_path_with_slashes(path): | ||
""" | ||
get path using forward slashes (e.g. replace back-slash by forward slash on Windows) | ||
""" | ||
return path.replace('\\', '/') |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
if __name__ == '__main__': | ||
print('Hello world') |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
if __name__ == '__main__': | ||
print('Hello world') |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
if __name__ == '__main__': | ||
print('Hello world') |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import os | ||
import unittest | ||
from pathlib import Path | ||
|
||
from src.code_size_counter import CodeSizeCounter | ||
|
||
|
||
class TestCodeSizeCounter(unittest.TestCase): | ||
""" | ||
class containing tests for CodeSizeCounter class | ||
""" | ||
|
||
def test_calculate_size_simple(self): | ||
code_size_counter = CodeSizeCounter(os.path.join(_get_tests_dir(), 'simple-test-dir'), ('txt',), False, ()) | ||
code_size = code_size_counter.calculate_size() | ||
|
||
self.assertEqual(3, code_size.total_files) | ||
self.assertEqual(2374, code_size.total_size) | ||
self.assertEqual(35, code_size.total_lines) | ||
|
||
def test_calculate_size_complex(self): | ||
pass | ||
|
||
def test_calculate_size_exclude_files(self): | ||
excluded_directories = map(lambda ex: os.path.join(_get_tests_dir(), 'exclude-test-dir', ex), | ||
[os.path.join('dir', 'dir-ex2'), 'dir-ex']) | ||
|
||
code_size_counter = CodeSizeCounter(os.path.join(_get_tests_dir(), 'exclude-test-dir'), ('py',), False, | ||
tuple(excluded_directories)) | ||
code_size = code_size_counter.calculate_size() | ||
|
||
self.assertEqual(2, code_size.total_files) | ||
self.assertEqual(104, code_size.total_size) | ||
self.assertEqual(4, code_size.total_lines) | ||
|
||
def test_calculate_size_no_matching_files(self): | ||
pass | ||
|
||
|
||
def _get_tests_dir(): | ||
""" | ||
get path to /tests directory | ||
:return: path to /tests directory | ||
""" | ||
return Path(__file__).parent.absolute() | ||
|
||
|
||
if __name__ == '__main__': | ||
unittest.main() |
Oops, something went wrong.