Repository: Para-Lang/para-compiler (language: python)

Issue:
Add public Para-C Compiler to pypi.org
**Summary:**
1. Make the Para-C Compiler modular and library-like with an interface implementation, which could be an independent 
    implementation (`parac-cli.py`), which will handle the CLI, while the core compiler can be used as wanted with the functions 
    provided as required. 
    
    This also would include the Compiler being usable as an importable by importing the `parac` module, with the sub-modules 
    `compiler` and `preprocessor`. This could also allow for integrating into other environments or specialised customisation, by also
    possibly overwriting methods and adding new content using a simple new module.
2. Add automatic publishing of the module to `pypi.org` using Actions including the base library for a full functioning compiler. 

**Required Changes:**
- Modular Implementation (Changes of the source code to fit a modular style (more separation and ability to utilise functions as wanted
- Addition of a `pypi.org` targeted `README.md` for proper explanation and usage as a python module
- ~~Separation of the CLI into a new file called `parac-cli.py`, which should handle the cli independently without adding any additional implementation into the source files to preserve modularity.~~
- Separation of the CLI into a new module called `parac_cli`, which will be later on included as a new plugin for `parac` (#15)
[Tool Proposal]  Add public pypi module `cli` plugin for parac
**Summary:**
Add support for the `cli` implementation of Para-C, as a plugin that can be installed using:
```bash
python3 -m pip install parac[cli]
```
This means that the cli should be officially made into a separate module, which *implements* the `parac` module. This would also mean that functions inside the `parac` module targetted at the cli would be moved to the cli module for proper separation.

Issue in addition to #14 
[Tool Proposal]  Add public pypi module `cli` plugin for parac
**Summary:**
Add support for the `cli` implementation of Para-C, as a plugin that can be installed using:
```bash
python3 -m pip install parac[cli]
```
This means that the cli should be officially made into a separate module, which *implements* the `parac` module. This would also mean that functions inside the `parac` module targetted at the cli would be moved to the cli module for proper separation.

Issue in addition to #14 


Discussion / hints:
Implemented structure-changes with 03fa12660cca15e262ce214a4e3a7a209dea0eef, 0dcfd4ec7be2cb40b2739f001c47e634fb7054d0 and 9abdc189cabe3fe431a9be8d42dfaba5e1438440
Implemented distinction between module and distribution versions of utilising the Para-C compiler. This can be now checked using the constant values from `const.py`: `DIST_VERSION: bool` and `MODULE_VERSION: bool`



Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bbe12baf..13e3509f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,11 +9,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## [Unreleased]
 
+### Added
+
+### Changed
+
+### Removed
+
+## [v0.1.dev2] - 2021-07-23
+
 ### Added
 - Pure syntax check command (`parac syntax-check`, For info see #9 and #10)
 - Added const file `const.py` for containing constant values that are used
   throughout the module
-- Structure as proper module, so it can be distributed to `pypi` as module as well  
+- pypi Module Structure with new parent `parac`. Releases from now on will be uploaded to pypi.org as module
 - Distinction between distribution and module version and const Values 
   (`const.py`) `DIST_VERSION` and `MODULE_VERSION` for separating Distribution
   and Module/Source-Code Version.
@@ -52,9 +60,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
     are treated like `InterruptErrors` and will abort the compilation
 - Class `InternalErrorInfo` for saving information about an exception causing an `InternalError`
 - Property `origin_exc` to `InterrruptError` for saving the original exception instance that was raised.
+- New module `parac_cli` for implementing the `parac` module
 
 ### Changed
-- Restructured module and added new parent module `parac` for both compiler and preprocessor.
+- Module Structure and added new parent module `parac` for both compiler and preprocessor.
 - Token Classes and refined items  
 - In `build-exe.py`; Implemented `pathlib.Path` usage and proper handling changed based on the new structure
 - Antlr4 Grammar file to include Pre-Processor statements and 
@@ -93,5 +102,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Set up a testing structure for the compiler using `pytest`
 - Created testing files for the parser and lexer
 
-[unreleased]: https://github.com/Para-C/Para-C/compare/v0.1.dev1...antlr4-dev
+[unreleased]: https://github.com/Para-C/Para-C/compare/v0.1.dev2...antlr4-dev
+[v0.1.dev2]: https://github.com/Para-C/Para-C/compare/v0.1.dev1...v0.1.dev2
 [v0.1.dev1]: https://github.com/Para-C/Para-C/releases/tag/v0.1.dev1
\ No newline at end of file
diff --git a/inno-setup.iss b/inno-setup.iss
index 494b7769..30fbac9f 100644
--- a/inno-setup.iss
+++ b/inno-setup.iss
@@ -1,5 +1,5 @@
 #define ExeName "Para-C"
-#define ExeVersion "0.1.dev1"
+#define ExeVersion "0.1.dev2"
 #define ExePublisher "Luna-Klatzer"
 #define ExeURL "https://github.com/Para-C/Para-C.git"
 #define ExeExeName "parac.exe"
diff --git a/src/README.md b/src/README.md
index 6c9223b0..f34924f2 100644
--- a/src/README.md
+++ b/src/README.md
@@ -95,11 +95,17 @@ automatically false):
  - `DIST_VERSION: bool` - If `True` it's the distribution version
  - `MODULE_VERSION: bool` - If `True` it's the module version
 
-*Note: The most notable difference between the two options is the location of
-the lib folder, containing the C implementation. The folder is in the module
-version in the root folder of the **module**, aka. where setup.py is/was located
-and in the distribution folder in the main **root** folder. The entry point is
-in this case in the ./bin/ folder*
+**Notes:**
+1. The most notable difference between the two options is the location of
+   the lib folder, containing the C implementation. The folder is in the module
+   version in the root folder of the **module**, aka. where setup.py is/was located
+   and in the distribution folder in the main **root** folder. The entry point is
+   in this case in the ./bin/ folder
+2. Currently, both commands `run` and `compile` will raise after 15% completion 
+   `TypeError: cannot unpack non-iterable NoneType object`, which is expected,
+   since the code-generation is not completed and therefore the compilation
+   can not be finished as wanted.
+
 
 ### CLI
 ...
@@ -107,9 +113,7 @@ in this case in the ./bin/ folder*
 #### Naming convention
 All functions associated with the cli and implement special logging (take input,
 or create graphic output) must be prefixed with `cli_` for vars and functions, 
-and `CLI` for classes. 
-
-This does not apply to items in the `cli.py` file
+and `CLI` for classes.
 
 ### Compiler
 ...
diff --git a/src/build-exe.py b/src/build-exe.py
index a3f955bc..385e67f8 100644
--- a/src/build-exe.py
+++ b/src/build-exe.py
@@ -19,7 +19,7 @@
 EXAMPLE_PATH: Path = BASE_PATH / "examples"
 
 # get entry path for the compiler
-entry_path: Path = pkg_resources.resource_filename(__name__, "cli.py")
+entry_path: Path = pkg_resources.resource_filename(__name__, "entry_cli.py")
 icon_path: Path = BASE_PATH / "img" / "parac.ico"
 
 required: List[Path] = [
diff --git a/src/cli.py b/src/entry_cli.py
similarity index 74%
rename from src/cli.py
rename to src/entry_cli.py
index 6e5829ba..0af075c3 100644
--- a/src/cli.py
+++ b/src/entry_cli.py
@@ -1,8 +1,8 @@
 # coding=utf-8
 """ Main compile function that calls the CLI """
-from parac.compiler import __main__
+import parac_cli
 from parac.logging import init_rich_console
 
 if __name__ == '__main__':
     init_rich_console()
-    __main__.cli()
+    parac_cli.cli_entry()
diff --git a/src/parac/__init__.py b/src/parac/__init__.py
index 43ab3716..8358bfca 100644
--- a/src/parac/__init__.py
+++ b/src/parac/__init__.py
@@ -1,5 +1,5 @@
 # coding=utf-8
-""" Init file for managing both compiler and preprocessor """
+""" Para-C (parac) Module for the Para-C Compiler and Pre-Processor """
 
 __title__ = "parac"
 __description__ = "Para-C Module"
@@ -7,7 +7,7 @@
 __author__ = "Luna Klatzer"
 __author_email__ = "luna.klatzer@gmail.com"
 __license__ = "GNU GENERAL PUBLIC LICENSE v3.0"
-__version__ = "v0.1.dev1"
+__version__ = "v0.1.dev2"
 __code_name__ = ""
 __release__ = f"{__code_name__} {__version__}"
 __copyright__ = "Luna Klatzer"
@@ -55,6 +55,7 @@
     'DEFAULT_BUILD_PATH',
     'DEFAULT_CONFIG',
     'C_COM_EXISTENCE_OVERWRITE',
+    'RUNTIME_COMPILER',
     *exceptions.__all__,
     *const.__all__,
     *MODULES
@@ -65,3 +66,7 @@
 
 colorama.init(autoreset=True)
 lib_logging.getLogger(__name__).addHandler(lib_logging.NullHandler())
+
+# An instance of the compiler, which should be generally used in the module
+# due to the logging logic
+RUNTIME_COMPILER: compiler.ParacCompiler = compiler.ParacCompiler()
diff --git a/src/parac/abc/base_ctx.py b/src/parac/abc/base_ctx.py
index a8487228..ed7b909d 100644
--- a/src/parac/abc/base_ctx.py
+++ b/src/parac/abc/base_ctx.py
@@ -2,18 +2,18 @@
 """
 Base Program ABC classes for the Pre-Processor and Compiler Context Classes
 """
-
+import logging
+from abc import ABC, abstractmethod
+from os import PathLike
+from typing import Union, Dict, Any
+import antlr4
 
 __all__ = [
     'FileRunContext',
     'ProgramRunContext'
 ]
 
-from abc import ABC, abstractmethod
-from os import PathLike
-from typing import Union, Dict, Any
-
-import antlr4
+logger = logging.getLogger(__name__)
 
 
 class FileRunContext(ABC):
@@ -159,10 +159,9 @@ async def get_stream_and_parse(
 
         :param file_path: Path to the file
         :param enable_out: If set to True errors, warnings and info will be
-                   logged onto the console using the local logger
-                   instance. If an exception is raised or error is
-                   encountered, it will be reraised with the
-                   FailedToProcessError.
+        logged onto the console using the local logger instance. If an
+        exception is raised or error is encountered, it will be reraised with
+        the FailedToProcessError.
         :returns: The FilePreProcessorContext instance for the file
         """
         ...
@@ -179,12 +178,11 @@ async def parse_single_file(
 
         :param stream: The Antlr4 InputStream which represents a string stream
         :param relative_file_name: Relative name of the file (fetch-able
-                                   using get_relative_file_name)
+        using get_relative_file_name)
         :param enable_out: If set to True errors, warnings and info will be
-                           logged onto the console using the local logger
-                           instance. If an exception is raised or error is
-                           encountered, it will be reraised with the
-                           FailedToProcessError.
+        logged onto the console using the local logger instance. If an
+        exception is raised or error is encountered, it will be reraised with
+        the FailedToProcessError.
         :returns: The generated FilePreProcessorContext instance
         """
         ...
@@ -198,15 +196,12 @@ async def parse_entry_file(
         to the generated context for the file.
 
         :param enable_out: If set to True errors, warnings and info will be
-                           logged onto the console using the local logger
-                           instance. If an exception is raised or error is
-                           encountered, it will be reraised with the
-                           FailedToProcessError.
+        logged onto the console using the local logger instance. If an
+        exception is raised or error is encountered, it will be reraised with
+        the FailedToProcessError.
         """
-        from ..compiler import para_compiler
-
         entry_path = self._process.entry_file_path
-        para_compiler.logger.debug(f"Parsing entry-file ({entry_path})")
+        logger.debug(f"Parsing entry-file ({entry_path})")
 
         entry_ctx = await self.get_stream_and_parse(entry_path, enable_out)
         self.set_entry_ctx(entry_ctx)
diff --git a/src/parac/abc/base_error_handler.py b/src/parac/abc/base_error_handler.py
index 31f945b4..4a733be6 100644
--- a/src/parac/abc/base_error_handler.py
+++ b/src/parac/abc/base_error_handler.py
@@ -54,7 +54,7 @@ def __init__(self, reraise: bool):
         Initialises the instance
 
         :param reraise: If set to True the error listener will reraise errors
-                        and not just log them
+        and not just log them
         """
         self.reraise = reraise
 
diff --git a/src/parac/compiler/__init__.py b/src/parac/compiler/__init__.py
index 36905c00..0324e974 100644
--- a/src/parac/compiler/__init__.py
+++ b/src/parac/compiler/__init__.py
@@ -1,7 +1,5 @@
 # coding=utf-8
 """ Compiler for the Para-C programming language """
-from . import __main__
-from .__main__ import *
 from . import optimiser
 from . import parser
 from . import error_handler
@@ -17,7 +15,6 @@
     *compiler.__all__,
     *ctx.__all__,
     *process.__all__,
-    *__main__.__all__,
     'optimiser',
     'parser',
     'error_handler',
diff --git a/src/parac/compiler/compiler.py b/src/parac/compiler/compiler.py
index 356b1ad9..5e3e5571 100644
--- a/src/parac/compiler/compiler.py
+++ b/src/parac/compiler/compiler.py
@@ -60,13 +60,13 @@ def init_logging_session(
         and adds the formatting defaults
 
         :param log_path: Path where the log file should be placed. If None
-                         logging to files will be ignored
+        logging to files will be ignored
         :param level: Level the logger should be initialised with.
-                      Defaults to INFO
+        Defaults to INFO
         :param print_banner: If set to True the logging banner will be printed
         :param banner_name: The name used for the logging banner
-        :param additional_newline: If set to True an additional newline
-                                   will be added before the logging banner
+        :param additional_newline: If set to True an additional newline will be
+        added before the logging banner
         """
         if print_banner:
             print_log_banner(banner_name, additional_newline)
@@ -116,10 +116,9 @@ async def parse(
 
         :param input_stream: The token stream of the file
         :param enable_out: If set to True errors, warnings and info will be
-                           logged onto the console using the local logger
-                           instance. If an exception is raised or error is
-                           encountered, it will be reraised with the
-                           FailedToProcessError.
+        logged onto the console using the local logger instance. If an
+        exception is raised or error is encountered, it will be reraised with
+        the FailedToProcessError.
         :returns: The compilationUnit (file) context
         """
         # Error handler which uses the default error strategy to handle the 
@@ -154,10 +153,9 @@ async def validate_syntax(
 
         :param process: The BasicProcess containing the path to the entry-file
         :param enable_out: If set to True errors, warnings and info will be
-                           logged onto the console using the local logger
-                           instance. If an exception is raised or error is
-                           encountered, it will be reraised with the
-                           FailedToProcessError.
+        logged onto the console using the local logger instance. If an
+        exception is raised or error is encountered, it will be reraised with
+        the FailedToProcessError.
         :returns: True if the syntax check was successful else False
         """
         file_stream: antlr4.FileStream = get_file_stream(
@@ -186,9 +184,9 @@ async def validate_syntax(
             # TODO! Add proper error handling and logging
             return False
 
-        from . import para_compiler
+        from .. import RUNTIME_COMPILER
 
-        if para_compiler.stream_handler.errors > 0:
+        if RUNTIME_COMPILER.stream_handler.errors > 0:
             return False
         return True
 
diff --git a/src/parac/compiler/ctx.py b/src/parac/compiler/ctx.py
index 99d57879..2e70edab 100644
--- a/src/parac/compiler/ctx.py
+++ b/src/parac/compiler/ctx.py
@@ -157,12 +157,12 @@ async def gen_source(self) -> Dict[str, Dict[str, FileCompilationContext]]:
         Generates the source C-code from the tokens stored inside the class.
 
         :returns: Dict[
-                  str - Name of the file (Relative name),
-                  Dict[
-                    str - The code-string,
-                    FileCompilationContext - The context of the file
-                  ]
-                 ]
+          str - Name of the file (Relative name),
+          Dict[
+            str - The code-string,
+            FileCompilationContext - The context of the file
+          ]
+         ]
         """
         ...
 
@@ -172,10 +172,9 @@ async def process_program(self, enable_out: bool) -> None:
         for generating the finished code.
 
         :param enable_out: If set to True errors, warnings and info will be
-                           logged onto the console using the local logger
-                           instance. If an exception is raised or error is
-                           encountered, it will be reraised with the
-                           FailedToProcessError.
+        logged onto the console using the local logger instance. If an
+        exception is raised or error is encountered, it will be reraised with
+        the FailedToProcessError.
         """
         await self.parse_entry_file(enable_out)
 
@@ -192,10 +191,9 @@ async def get_stream_and_parse(
 
         :param file_path: Path to the file
         :param enable_out: If set to True errors, warnings and info will be
-                   logged onto the console using the local logger
-                   instance. If an exception is raised or error is
-                   encountered, it will be reraised with the
-                   FailedToProcessError.
+        logged onto the console using the local logger instance. If an
+        exception is raised or error is encountered, it will be reraised with
+        the FailedToProcessError.
         :returns: The FilePreProcessorContext instance for the file
         """
         from .compiler import ParacCompiler
@@ -227,13 +225,12 @@ async def parse_single_file(
         Parses a single file and generates the FilePreProcessorContext
 
         :param stream: The Antlr4 InputStream which represents a string stream
-        :param relative_file_name: Relative name of the file (fetch-able
-                                   using get_relative_file_name)
+        :param relative_file_name: Relative name of the file (fetch-able using
+        get_relative_file_name)
         :param enable_out: If set to True errors, warnings and info will be
-                           logged onto the console using the local logger
-                           instance. If an exception is raised or error is
-                           encountered, it will be reraised with the
-                           FailedToProcessError.
+        logged onto the console using the local logger instance. If an
+        exception is raised or error is encountered, it will be reraised with
+        the FailedToProcessError.
         :returns: The generated FilePreProcessorContext instance
         """
         from .compiler import ParacCompiler
diff --git a/src/parac/compiler/error_handler.py b/src/parac/compiler/error_handler.py
index e6e671eb..76dcae58 100644
--- a/src/parac/compiler/error_handler.py
+++ b/src/parac/compiler/error_handler.py
@@ -26,7 +26,7 @@ def __init__(self, reraise: bool):
         Initialises the instance
 
         :param reraise: If set to True the error listener will reraise errors
-                        and not just log them
+        and not just log them
         """
         super().__init__(reraise)
 
diff --git a/src/parac/compiler/parser/listener.py b/src/parac/compiler/parser/listener.py
index 946c1914..407bc5bc 100644
--- a/src/parac/compiler/parser/listener.py
+++ b/src/parac/compiler/parser/listener.py
@@ -58,10 +58,9 @@ async def walk(self, enable_out: bool) -> None:
         events / goes through the tokens
 
         :param enable_out: If set to True errors, warnings and info will be
-                           logged onto the console using the local logger
-                           instance. If an exception is raised or error is
-                           encountered, it will be reraised with the
-                           FailedToProcessError.
+        logged onto the console using the local logger instance. If an
+        exception is raised or error is encountered, it will be reraised with
+        the FailedToProcessError.
         """
         logger.debug(
             "Walking through logic tree and generating the logic stream"
@@ -84,10 +83,9 @@ async def walk_and_generate_logic_stream(self, enable_out: bool) -> None:
         the compilation for the program
 
         :param enable_out: If set to True errors, warnings and info will be
-                           logged onto the console using the local logger
-                           instance. If an exception is raised or error is
-                           encountered, it will be reraised with the
-                           FailedToProcessError.
+        logged onto the console using the local logger instance. If an
+        exception is raised or error is encountered, it will be reraised with
+        the FailedToProcessError.
         """
         self._compiling = True
         await self.walk(enable_out)
diff --git a/src/parac/compiler/process.py b/src/parac/compiler/process.py
index a729ac6f..1818a71d 100644
--- a/src/parac/compiler/process.py
+++ b/src/parac/compiler/process.py
@@ -14,7 +14,7 @@
 from ..preprocessor.ctx import ProgramPreProcessorContext
 
 from .ctx import ProgramCompilationContext
-from ..util import (decode_if_bytes, cleanup_path, validate_file_ending,
+from ..util import (decode_if_bytes, cleanup_path_str, validate_file_ending,
                     validate_path_like)
 from ..exceptions import FileAccessError
 
@@ -40,10 +40,10 @@ def __init__(
         further usage.
 
         :param entry_file_path: The entry-file of the program. The compiler
-                                will use the working directory as base dir if
-                                the path is relative
+        will use the working directory as base dir if
+        the path is relative
         """
-        entry_file_path = cleanup_path(decode_if_bytes(entry_file_path))
+        entry_file_path = cleanup_path_str(decode_if_bytes(entry_file_path))
 
         # Getting the last element aka. the lowest level of the path
         _last_path_elem = entry_file_path.replace("/", "\\").split("\\")[-1]
@@ -67,7 +67,7 @@ def __init__(
             # current path meaning the work-directory path needs to be
             # appended. If that also fails it is an invalid path or permissions
             # are missing
-            absolute_path = cleanup_path(f"{os.getcwd()}\\{entry_file_path}")
+            absolute_path = cleanup_path_str(f"{os.getcwd()}\\{entry_file_path}")
             failed = False
             try:
                 validate_path_like(absolute_path)
@@ -110,10 +110,9 @@ async def validate_syntax(self, enable_out: bool) -> bool:
         errors while running
 
         :param enable_out: If set to True errors, warnings and info will be
-                           logged onto the console using the local logger
-                           instance. If an exception is raised or error is
-                           encountered, it will be reraised with the
-                           FailedToProcessError.
+        logged onto the console using the local logger instance. If an
+        exception is raised or error is encountered, it will be reraised with
+        the FailedToProcessError.
         :returns: True if the syntax check was successful else False
         """
         from .compiler import ParacCompiler
@@ -150,20 +149,20 @@ def __init__(
         process cancelled.
 
         :param entry_file_path: The entry-file of the program. The compiler
-                                will use the working directory as base dir
-                                if the path is relative
+        will use the working directory as base dir
+        if the path is relative
         :param build_path: The path to the output folder
         :param dist_path: The path to the dist folder
         :returns: The file name, the output build path, the output dist path
-                  and the arguments passed for the compilation
+        and the arguments passed for the compilation
         """
         super().__init__(entry_file_path, encoding)
 
         build_path: Union[str, PathLike] = decode_if_bytes(build_path)
         dist_path: Union[str, PathLike] = decode_if_bytes(dist_path)
 
-        self._build_path = cleanup_path(build_path)
-        self._dist_path = cleanup_path(dist_path)
+        self._build_path = cleanup_path_str(build_path)
+        self._dist_path = cleanup_path_str(dist_path)
         self._temp_files: List[str] = []
         self._temp_entry_file_path: Union[str, None] = None
         self._preprocessor_ctx = ProgramPreProcessorContext(self)
@@ -187,9 +186,9 @@ def temp_build_folder(self) -> Union[str, PathLike]:
         # due to the cleanup of paths in the initialisation
         from .. import SEPARATOR
         if self.build_path.endswith(SEPARATOR):
-            return cleanup_path(f"{self.build_path}temp")
+            return cleanup_path_str(f"{self.build_path}temp")
         else:
-            return cleanup_path(f"{self.build_path}{SEPARATOR}temp")
+            return cleanup_path_str(f"{self.build_path}{SEPARATOR}temp")
 
     @property
     def temp_dist_folder(self) -> Union[str, PathLike]:
@@ -198,9 +197,9 @@ def temp_dist_folder(self) -> Union[str, PathLike]:
         # due to the cleanup of paths in the initialisation
         from .. import SEPARATOR
         if self.dist_path.endswith(SEPARATOR):
-            return cleanup_path(f"{self.dist_path}temp")
+            return cleanup_path_str(f"{self.dist_path}temp")
         else:
-            return cleanup_path(f"{self.dist_path}{SEPARATOR}temp")
+            return cleanup_path_str(f"{self.dist_path}{SEPARATOR}temp")
 
     @property
     def temp_files(self) -> List[str]:
diff --git a/src/parac/logging.py b/src/parac/logging.py
index bd299418..384ff579 100644
--- a/src/parac/logging.py
+++ b/src/parac/logging.py
@@ -355,9 +355,19 @@ def make_italic(cls, value: str) -> str:
 
 
 def print_init_banner() -> None:
-    """ Creates the init screen string that can be printed """
+    """
+    Creates the init screen string that can be printed
+
+    Required init_rich_console to be called before it!
+    """
     from . import __version__
 
+    if get_rich_console() is None:
+        raise RuntimeError(
+            "Rich console was not initialised. Use init_rich_console to"
+            " utilise this function"
+        )
+
     if OVERWRITE_AVOID_PRINT_BANNER:
         return get_rich_console().print("\n", end="")
 
@@ -374,7 +384,15 @@ def print_init_banner() -> None:
 def print_abort_banner(process: str) -> None:
     """
     Prints a simple colored Exception banner showing it crashed / was aborted
+
+    Required init_rich_console to be called before it!
     """
+    if get_rich_console() is None:
+        raise RuntimeError(
+            "Rich console was not initialised. Use init_rich_console to"
+            " utilise this function"
+        )
+
     if OVERWRITE_AVOID_PRINT_BANNER:
         return get_rich_console().print("\n", end="")
 
@@ -391,9 +409,17 @@ def print_result_banner(
     Prints a simple colored banner screen showing the process finished and a
     result is available
 
+    Required init_rich_console to be called before it!
+
     :param name: The name that should be printed before the ' Result'
     :param success: If success if True then the banner will be in green
     """
+    if get_rich_console() is None:
+        raise RuntimeError(
+            "Rich console was not initialised. Use init_rich_console to"
+            " utilise this function"
+        )
+
     if OVERWRITE_AVOID_PRINT_BANNER:
         return get_rich_console().print("\n", end="")
 
@@ -408,14 +434,22 @@ def print_result_banner(
 def print_log_banner(name: str = "Compiler", newline: bool = True) -> None:
     """
     Prints a simple colored banner screen showing the logs are active and
-    the process started
+    the process started.
+
+    Required init_rich_console to be called before it!
     """
+    if get_rich_console() is None:
+        raise RuntimeError(
+            "Rich console was not initialised. Use init_rich_console to"
+            " utilise this function"
+        )
+
     if OVERWRITE_AVOID_PRINT_BANNER:
         return get_rich_console().print("\n", end="")
 
     if newline:
-        output_console.print("\n", end="")
-    output_console.rule(
+        get_rich_console().print("\n", end="")
+    get_rich_console().rule(
         f"[bold bright_cyan]{name} Logs[white]\n",
         style="bright_white rule.line"
     )
diff --git a/src/parac/preprocessor/__main__.py b/src/parac/preprocessor/__main__.py
index 4c1b95ae..56b2c2ad 100644
--- a/src/parac/preprocessor/__main__.py
+++ b/src/parac/preprocessor/__main__.py
@@ -4,8 +4,8 @@
 """
 from __future__ import annotations
 
+import logging
 from typing import TYPE_CHECKING, Dict
-from click.utils import WIN
 import antlr4
 
 from .python.ParaCPreProcessorParser import ParaCPreProcessorParser
@@ -16,12 +16,11 @@
     from .ctx import ProgramPreProcessorContext, FilePreProcessorContext
 
 __all__ = [
-    'SEPARATOR',
     'PreProcessor',
     'PreProcessorProcessResult'
 ]
 
-SEPARATOR = "\\" if WIN else "/"
+logger = logging.getLogger(__name__)
 
 
 class PreProcessorProcessResult:
@@ -73,14 +72,11 @@ async def parse(
 
         :param input_stream: The token stream of the file
         :param enable_out: If set to True errors, warnings and info will be
-                           logged onto the console using the local logger
-                           instance. If an exception is raised or error is
-                           encountered, it will be reraised with the
-                           FailedToProcessError.
+        logged onto the console using the local logger instance. If an
+        exception is raised or error is encountered, it will be reraised with
+        the FailedToProcessError.
         :returns: The compilationUnit (file) context
         """
-        from ..compiler import para_compiler
-
         # Error handler which uses the default error strategy to handle the
         # incoming antlr4 errors
         error_listener = PreProcessorErrorListener(enable_out)
@@ -91,19 +87,19 @@ async def parse(
         lexer.removeErrorListeners()
         lexer.addErrorListener(error_listener)
 
-        para_compiler.logger.debug("Lexing file and generating tokens")
+        logger.debug("Lexing file and generating tokens")
 
         # Parsing the lexer and generating a token stream
         stream = antlr4.CommonTokenStream(lexer)
 
-        para_compiler.logger.debug(
+        logger.debug(
             "Parsing the tokens and generating the logic tree"
         )
         # Parser which generates based on the top entry rule the logic tree
         parser = ParaCPreProcessorParser(stream)
         parser.removeErrorListeners()
         parser.addErrorListener(error_listener)
-        para_compiler.logger.debug(
+        logger.debug(
             "Finished generation of compilationUnit for the file"
         )
         return parser.compilationUnit()
diff --git a/src/parac/preprocessor/ctx.py b/src/parac/preprocessor/ctx.py
index 2bf08629..83ffd6de 100644
--- a/src/parac/preprocessor/ctx.py
+++ b/src/parac/preprocessor/ctx.py
@@ -144,7 +144,7 @@ async def make_temp_files(
         process_program()
 
         :returns: A tuple containing at 0 the path to the entry-file and at 1
-                  a list of all paths of all other files.
+        a list of all paths of all other files.
         """
         ...
 
@@ -156,10 +156,9 @@ async def process_program(
         for generating the finished code.
 
         :param enable_out: If set to True errors, warnings and info will be
-                           logged onto the console using the local logger
-                           instance. If an exception is raised or error is
-                           encountered, it will be reraised with the
-                           FailedToProcessError.
+        logged onto the console using the local logger instance. If an
+        exception is raised or error is encountered, it will be reraised with
+        the FailedToProcessError.
         :returns: A PreProcessorProcessResult instance
         """
         await self.parse_entry_file(enable_out)
@@ -180,10 +179,9 @@ async def get_stream_and_parse(
 
         :param file_path: Path to the file
         :param enable_out: If set to True errors, warnings and info will be
-                   logged onto the console using the local logger
-                   instance. If an exception is raised or error is
-                   encountered, it will be reraised with the
-                   FailedToProcessError.
+        logged onto the console using the local logger instance. If an
+        exception is raised or error is encountered, it will be reraised with
+        the FailedToProcessError.
         :returns: The FilePreProcessorContext instance for the file
         """
         from ..compiler import ParacCompiler
@@ -216,12 +214,11 @@ async def parse_single_file(
 
         :param stream: The Antlr4 InputStream which represents a string stream
         :param relative_file_name: Relative name of the file (fetch-able
-                                   using get_relative_file_name)
+        using get_relative_file_name)
         :param enable_out: If set to True errors, warnings and info will be
-                           logged onto the console using the local logger
-                           instance. If an exception is raised or error is
-                           encountered, it will be reraised with the
-                           FailedToProcessError.
+        logged onto the console using the local logger instance. If an
+        exception is raised or error is encountered, it will be reraised with
+        the FailedToProcessError.
         :returns: The generated FilePreProcessorContext instance
         """
         from .listener import Listener
diff --git a/src/parac/preprocessor/error_handler.py b/src/parac/preprocessor/error_handler.py
index b23224b9..5071ec42 100644
--- a/src/parac/preprocessor/error_handler.py
+++ b/src/parac/preprocessor/error_handler.py
@@ -1,18 +1,21 @@
 # coding=utf-8
 """ Error handler for the preprocessor parser and lexer """
+import logging
 from typing import Union
-from ..abc import BaseErrorListener
-
 from antlr4.error.Errors import (InputMismatchException,
                                  FailedPredicateException,
                                  RecognitionException,
                                  LexerNoViableAltException,
                                  NoViableAltException)
 
+from ..abc import BaseErrorListener
+
 __all__ = [
     'PreProcessorErrorListener',
 ]
 
+logger = logging.getLogger(__name__)
+
 
 class PreProcessorErrorListener(BaseErrorListener):
     """ Error-Listener for the PreProcessor """
@@ -22,7 +25,7 @@ def __init__(self, reraise: bool):
         Initialises the instance
 
         :param reraise: If set to True the error listener will reraise errors
-                        and not just log them
+        and not just log them
         """
         super().__init__(reraise)
 
@@ -91,7 +94,5 @@ def syntaxError(
         Method which will be called if the ANTLR4 Lexer or Parser detect
         an error inside the program
         """
-        from ..compiler import para_compiler
-
         # TODO! Add proper error handling
-        para_compiler.logger.error(f"At line: {line}, column: {column} - {msg}")
+        logger.error(f"At line: {line}, column: {column} - {msg}")
diff --git a/src/parac/preprocessor/listener.py b/src/parac/preprocessor/listener.py
index 168fb983..2812e987 100644
--- a/src/parac/preprocessor/listener.py
+++ b/src/parac/preprocessor/listener.py
@@ -1,7 +1,9 @@
 # coding=utf-8
 """ Logic Tree Listener for the Para-C Pre-Processor """
+import logging
 from typing import List
 import antlr4
+import logging
 
 from .abc import PreProcessorLogicToken
 from .python import ParaCPreProcessorListener
@@ -12,6 +14,7 @@
     'Listener'
 ]
 
+logger = logging.getLogger(__name__)
 _p = parser.ParaCPreProcessorParser
 
 
@@ -50,14 +53,11 @@ async def walk_and_process_directives(self, enable_out: bool) -> None:
         the preprocessor processing steps
 
         :param enable_out: If set to True errors, warnings and info will be
-                           logged onto the console using the local logger
-                           instance. If an exception is raised or error is
-                           encountered, it will be reraised with the
-                           FailedToProcessError.
+        logged onto the console using the local logger instance. If an
+        exception is raised or error is encountered, it will be reraised with
+        the FailedToProcessError.
         """
-        from ..compiler import para_compiler
-
-        para_compiler.logger.debug(
+        logger.debug(
             "Walking through the logic tree and generating logic stream"
         )
         self._enable_out = enable_out
diff --git a/src/parac/util/c_compiler.py b/src/parac/util/c_compiler.py
index 78490e1f..490a81ea 100644
--- a/src/parac/util/c_compiler.py
+++ b/src/parac/util/c_compiler.py
@@ -4,7 +4,7 @@
 import os
 import logging
 
-from .pathtools import cleanup_path, decode_if_bytes
+from .pathtools import cleanup_path_str, decode_if_bytes
 from ..exceptions import FilePermissionError, CCompilerNotFoundError
 from ..logging import get_rich_console as console
 
@@ -46,7 +46,7 @@ def cli_initialise_c_compiler() -> None:
         "[/bold bright_cyan]"
     )
     console().print('\n', end="")
-    path = cleanup_path(decode_if_bytes(_input))
+    path = cleanup_path_str(decode_if_bytes(_input))
 
     # it exists
     if not os.access(_input, os.F_OK):
diff --git a/src/parac/util/decorators.py b/src/parac/util/decorators.py
index a298cf9e..3c1dd2b0 100644
--- a/src/parac/util/decorators.py
+++ b/src/parac/util/decorators.py
@@ -42,19 +42,17 @@ def abortable(
 
     :param _func: Function to apply the decorator
     :param reraise: If set to True, any exception will be reraised. If False,
-                    it will close the program and write the error onto the
-                    console.
+    it will close the program and write the error onto the console.
     :param preserve_exception: If set to True, the original exception will be
-                               returned and not the wrapped exception using
-                               InternalError or InterruptError
+    returned and not the wrapped exception using InternalError or
+    InterruptError
     :param abort_on_internal_errors: If set to True when receiving an
-                                     InternalError it will treat it as a call
-                                     for aborting the process. This means it
-                                     will stop the program and print the
-                                     abort banner if print_abort is True.
+    InternalError it will treat it as a call for aborting the process. This
+    means it will stop the program and print the abort banner if print_abort is
+    True.
     :param print_abort: If True, it will print the abort banner when closing
     :param step: The step that should be passed onto print_abort_banner.
-                 Only valid argument when print_abort is True
+    Only valid argument when print_abort is True
     """
 
     def _decorator(func):
@@ -66,7 +64,7 @@ def _handle_abort(print_out: bool):
                 exit(1)
 
             try:
-                from ..compiler import para_compiler
+                from .. import RUNTIME_COMPILER
                 try:
                     return func(*args, **kwargs)
                 except InterruptError:
@@ -79,8 +77,8 @@ def _handle_abort(print_out: bool):
                         raise InterruptError(exc=e) from e
 
                 except ParacCompilerError as e:
-                    if not para_compiler.log_initialised:
-                        para_compiler.init_logging_session()
+                    if not RUNTIME_COMPILER.log_initialised:
+                        RUNTIME_COMPILER.init_logging_session()
 
                     log_traceback(
                         level="critical",
@@ -93,8 +91,8 @@ def _handle_abort(print_out: bool):
                         raise InterruptError(exc=e) from e
 
                 except Exception as e:
-                    if not para_compiler.log_initialised:
-                        para_compiler.init_logging_session()
+                    if not RUNTIME_COMPILER.log_initialised:
+                        RUNTIME_COMPILER.init_logging_session()
 
                     if preserve_exception:
                         raise e
@@ -129,13 +127,13 @@ def requires_init(_func=None):
     def _decorator(func):
         @functools.wraps(func)
         def _wrapper(*args, **kwargs):
-            from ..compiler import para_compiler
+            from .. import RUNTIME_COMPILER
             from .. import C_COM_EXISTENCE_OVERWRITE
             from . import (is_c_compiler_ready, cli_initialise_c_compiler)
 
             if not is_c_compiler_ready() and not C_COM_EXISTENCE_OVERWRITE:
-                if not para_compiler.log_initialised:
-                    para_compiler.init_logging_session()
+                if not RUNTIME_COMPILER.log_initialised:
+                    RUNTIME_COMPILER.init_logging_session()
 
                 init_rich_console()
                 console().print('')
diff --git a/src/parac/util/pathtools.py b/src/parac/util/pathtools.py
index 48cbea85..99df0d62 100644
--- a/src/parac/util/pathtools.py
+++ b/src/parac/util/pathtools.py
@@ -12,7 +12,7 @@
     "validate_file_ending",
     "validate_path_like",
     "decode_if_bytes",
-    "cleanup_path",
+    "cleanup_path_str",
     "check_valid_path_name",
     "get_relative_file_name"
 ]
@@ -34,16 +34,19 @@ def validate_path_like(path_like: Union[os.PathLike, str]) -> None:
     :raises EntryFileNotFoundError: If the file can't be found
     :raises EntryFilePermissionError: If the file can't be read from
     """
-    if not os.access(path_like, os.R_OK):  # Can be read
-        if not os.access(path_like, os.F_OK):  # Exists
-            raise FileNotFoundError(
-                f"Failed to read entry-point '{path_like}'."
-                f" File does not exist!"
-            )
-        else:
-            raise FilePermissionError(
-                f"Missing file reading permissions for ''{path_like}'"
-            )
+    # Due to an empty string '' being recognised as existing it will be
+    # excluded from the check
+    valid_in = str(path_like).strip() != ''
+
+    if not valid_in or not os.access(path_like, os.F_OK):  # Exists
+        raise FileNotFoundError(
+            f"Failed to read entry-point '{path_like}'."
+            f" File does not exist!"
+        )
+    elif not os.access(path_like, os.R_OK):  # Can be read:
+        raise FilePermissionError(
+            f"Missing file reading permissions for ''{path_like}'"
+        )
 
 
 def decode_if_bytes(
@@ -53,13 +56,13 @@ def decode_if_bytes(
     """ Decodes the passed PathLike if it is in bytes """
     if type(byte_like) is str:
         return byte_like
-    elif type(byte_like) is bytes or isinstance(bytes, byte_like):
+    elif type(byte_like) is bytes or isinstance(byte_like, bytes):
         return byte_like.decode(encoding)
     else:
         return byte_like
 
 
-def cleanup_path(_p: str) -> str:
+def cleanup_path_str(_p: str) -> str:
     """ Cleans the path for the specific current os """
     if WIN:
         _p = _p.replace("/", SEPARATOR).replace("\\\\", SEPARATOR)
@@ -82,9 +85,9 @@ def check_valid_path_name(
 
     :param path: A path-like or file-name which should be checked
     :param win_path: If explicitly set to True, the path will be checked if it
-                     was a windows path, even if it's a os of a different kind
+    was a windows path, even if it's a os of a different kind
     """
-    path = cleanup_path(path)
+    path = cleanup_path_str(path)
     path = path.replace(SEPARATOR, '')
 
     if WIN or win_path:
@@ -118,14 +121,14 @@ def get_relative_file_name(
     match the file_name passed RuntimeError will be raised
 
     :param file_name: Simple file name which cannot contain < , > , : , " , / ,
-                      \\ (Escaped) , | , ? , *
+    \\ (Escaped) , | , ? , *
     :param file_path: Full path of the file
     :param base_path: Full base path for the working directory
     :param win_path: If explicitly set to True, the path will be checked if it
-                     was a windows path, even if it's a os of a different kind
+    was a windows path, even if it's a os of a different kind
     """
-    file_path = cleanup_path(file_path)
-    base_path = cleanup_path(base_path)
+    file_path = cleanup_path_str(file_path)
+    base_path = cleanup_path_str(base_path)
 
     if base_path not in file_path:
         raise RuntimeError(
diff --git a/src/parac_cli/__init__.py b/src/parac_cli/__init__.py
new file mode 100644
index 00000000..dcdbe2d9
--- /dev/null
+++ b/src/parac_cli/__init__.py
@@ -0,0 +1,15 @@
+# coding=utf-8
+""" CLI Implementation of the parac module """
+
+__title__ = "parac"
+__description__ = "Para-C Module"
+__url__ = "https://github.com/Para-C/Para-C/"
+__author__ = "Luna Klatzer"
+__author_email__ = "luna.klatzer@gmail.com"
+__license__ = "GNU GENERAL PUBLIC LICENSE v3.0"
+__version__ = "v0.1.dev2"
+__code_name__ = ""
+__release__ = f"{__code_name__} {__version__}"
+__copyright__ = "Luna Klatzer"
+
+from .__main__ import *
diff --git a/src/parac/compiler/__main__.py b/src/parac_cli/__main__.py
similarity index 75%
rename from src/parac/compiler/__main__.py
rename to src/parac_cli/__main__.py
index ed55cb51..6f9a4f22 100644
--- a/src/parac/compiler/__main__.py
+++ b/src/parac_cli/__main__.py
@@ -1,57 +1,60 @@
 # coding=utf-8
 """ Main file of the Para-Compiler"""
-import shutil
 import time
 import asyncio
-from typing import Union, Tuple
+from pathlib import Path
+from typing import Union
 import click
 import colorama
 import logging
-import os
 from os import PathLike
 from rich.progress import Progress
 
-from ..exceptions import InvalidArgumentsError
-from ..const import DEFAULT_BUILD_PATH, DEFAULT_DIST_PATH
-from ..util import (cli_keep_open_callback, escape_ansi_args,
-                    requires_init, is_c_compiler_ready,
-                    cli_initialise_c_compiler, abortable)
-from ..logging import get_rich_console as console, print_result_banner, \
-    cli_create_prompt, cli_format_default, init_rich_console, print_init_banner
-from .compiler import ParacCompiler
-from .process import ProgramCompilationProcess, BasicProcess, FinishedProcess
+from parac import RUNTIME_COMPILER
+from parac.exceptions import InvalidArgumentsError
+from parac.util import (cli_keep_open_callback, escape_ansi_args,
+                        requires_init, is_c_compiler_ready,
+                        cli_initialise_c_compiler, abortable)
+from parac.logging import (get_rich_console as console, print_result_banner,
+                           cli_create_prompt, cli_format_default,
+                           init_rich_console, print_init_banner)
+from parac.compiler import (ProgramCompilationProcess,
+                            BasicProcess, FinishedProcess)
+from .utils import cli_run_output_dir_validation, cli_resolve_path
 
 __all__ = [
-    'para_compiler',
-    'create_process',
-    'run_output_dir_validation',
-    'run_process_with_logging',
-    'cli',
-    'parac_compile',
+    'cli_create_process',
+    'cli_run_output_dir_validation',
+    'cli_run_process_with_logging',
+    'cli_entry',
+    'cli_parac_compile',
     'ParacCLI'
 ]
 
 logger = logging.getLogger(__name__)
 colorama.init(autoreset=True)
-para_compiler = ParacCompiler()
 
 
 @abortable(step="Setup", reraise=True, preserve_exception=True)
-def create_process(
-        file: Union[str, PathLike],
+def cli_create_process(
+        file: Union[str, PathLike, Path],
         encoding: str,
-        log_path: Union[str, PathLike],
-        build_path: str,
-        dist_path: str
+        log_path: Union[str, PathLike, Path],
+        build_path: Union[str, PathLike, Path],
+        dist_path: Union[str, PathLike, Path]
 ) -> ProgramCompilationProcess:
     """
     Creates a compilation process, which can be used for compiling Para-C code
     and returns it.
     Activates logging on default
     """
-    if not para_compiler.log_initialised:
-        para_compiler.init_logging_session(log_path)
+    if not RUNTIME_COMPILER.log_initialised:
+        RUNTIME_COMPILER.init_logging_session(log_path)
 
+    # Resolving path and stripping whitespaces
+    file: str = cli_resolve_path(file).strip()
+    build_path: str = cli_resolve_path(build_path).strip()
+    dist_path: str = cli_resolve_path(dist_path).strip()
     return ProgramCompilationProcess(file, encoding, build_path, dist_path)
 
 
@@ -65,90 +68,24 @@ def create_basic_process(
     returns it.
     Activates logging on default
     """
-    if not para_compiler.log_initialised:
-        para_compiler.init_logging_session(log_path)
+    if not RUNTIME_COMPILER.log_initialised:
+        RUNTIME_COMPILER.init_logging_session(log_path)
 
     return BasicProcess(file, encoding)
 
 
-@abortable(step="Validating Output", reraise=True)
-def _err_dir_already_exists(folder: Union[str, PathLike]) -> bool:
-    """ Asks the user whether the build folder should be overwritten """
-    _input = console().input(
-        f"[bright_yellow] > [bright_white]The {folder} "
-        "folder already exists. Overwrite data? (y\\N): "
-    ).lower() == 'y'
-    return _input
-
-
-def _check_destination(
-        output_type: str,
-        default_path: Union[str, PathLike],
-        overwrite: bool
-) -> str:
-    """
-    Validates the destination and checks whether the specified output
-    folder is available. If the folder already exists it will show a prompt
-    to the user what should be done about the existing folder.
-
-    :returns: The path to the folder
-    """
-    output = default_path
-    if not os.path.exists(default_path):
-        os.mkdir(default_path)
-    elif len(os.listdir(default_path)) > 0:
-        # If the overwrite is set to False then a prompt will appear
-        if overwrite is False:
-            overwrite = _err_dir_already_exists(output_type)
-
-        if overwrite:
-            shutil.rmtree(default_path)
-            os.mkdir(default_path)
-        else:
-            counter = 2
-            while os.path.exists(f"{os.getcwd()}/{output_type}_{counter}"):
-                counter += 1
-            output = f"{os.getcwd()}/{output_type}_{counter}"
-            os.mkdir(output)
-    return output
-
-
-def run_output_dir_validation(
-        overwrite_build: bool,
-        overwrite_dist: bool
-) -> Tuple[str, str]:
-    """ Validates whether the output folder /build/ and /dist/ can be used
-
-    :param overwrite_build: If set to True if a build folder already exists
-                            it will be deleted and overwritten
-    :param overwrite_dist: If set to True if a dist folder already exists
-                           it will be deleted and overwritten
-    """
-    build_path = _check_destination(
-        "build",
-        DEFAULT_BUILD_PATH,
-        overwrite_build
-    )
-    dist_path = _check_destination(
-        "dist",
-        DEFAULT_DIST_PATH,
-        overwrite_dist
-    )
-    return build_path, dist_path
-
-
 async def run_process(p: ProgramCompilationProcess) -> FinishedProcess:
     """
     Runs the process and returns the finished compilation process
     Calls p.compile(), adds additional formatting and returns the result
     """
     finished_process = await p.compile()
-    if para_compiler.log_initialised:
+    if RUNTIME_COMPILER.log_initialised:
         print_result_banner()
     return finished_process
 
 
-async def run_process_with_logging(
+async def cli_run_process_with_logging(
         p: ProgramCompilationProcess
 ) -> FinishedProcess:
     """ Runs the compilation process with console logs and formatting """
@@ -174,7 +111,7 @@ async def run_process_with_logging(
                 current_progress = p
 
     console().print("\n", end="")
-    if para_compiler.log_initialised:
+    if RUNTIME_COMPILER.log_initialised:
         print_result_banner()
     return finished_process
 
@@ -193,12 +130,17 @@ async def run_process_with_logging(
 )
 @click.pass_context
 @abortable(reraise=False)
-def cli(*args, **kwargs):
-    """ Console Line Interface for the Para-C Compiler """
+def cli_entry(*args, **kwargs):
+    """
+    Console Line Interface for the Para-C Compiler
+    \f
+    Entry point for the CLI. This should be called when wanting to start the
+    cli. It will utilise the args passed to the program as the click arguments
+    """
     ParacCLI.cli(*args, **kwargs)
 
 
-@cli.command(name="c-init")
+@cli_entry.command(name="c-init")
 @click.option("--keep-open", is_flag=True)
 @abortable(reraise=False)
 def parac_c_init(*args, **kwargs):
@@ -208,7 +150,7 @@ def parac_c_init(*args, **kwargs):
     ParacCLI.parac_c_init(*args, **kwargs)
 
 
-@cli.command(name="compile")
+@cli_entry.command(name="compile")
 @click.option("--keep-open", is_flag=True)
 @click.option(
     "-f",
@@ -278,12 +220,12 @@ def parac_c_init(*args, **kwargs):
     help="If set the compiler will add additional debug information"
 )
 @abortable(reraise=False)
-def parac_compile(*args, **kwargs):
+def cli_parac_compile(*args, **kwargs):
     """ Compile a Para-C program to C or executable """
     ParacCLI.parac_compile(*args, **kwargs)
 
 
-@cli.command(name="run")
+@cli_entry.command(name="run")
 @click.option("--keep-open", is_flag=True)
 @click.option(
     "-f",
@@ -342,7 +284,7 @@ def parac_run(*args, **kwargs):
     ParacCLI.parac_run(*args, **kwargs)
 
 
-@cli.command(name="syntax-check")
+@cli_entry.command(name="syntax-check")
 @click.option("--keep-open", is_flag=True)
 @click.option(
     "-f",
@@ -401,7 +343,7 @@ def cli(ctx: click.Context, version, *args, **kwargs):
 
         out = console()
         if version:
-            from .. import __version__, __title__
+            from parac import __version__, __title__
             return out.print(' '.join([__title__.title(), __version__]))
         else:
             print_init_banner()
@@ -420,8 +362,8 @@ def cli(ctx: click.Context, version, *args, **kwargs):
     @escape_ansi_args
     def parac_c_init():
         """ Initialises the C compiler """
-        if not para_compiler.log_initialised:
-            para_compiler.init_logging_session(print_banner=False)
+        if not RUNTIME_COMPILER.log_initialised:
+            RUNTIME_COMPILER.init_logging_session(print_banner=False)
         logger.info(
             'Reinitialising' if is_c_compiler_ready() else 'Initialising'
             " Para-C Compiler"
@@ -446,8 +388,8 @@ def parac_compile(
         CLI interface for the parac_compile command.
         Will create a compilation-process and run it
         """
-        if not para_compiler.log_initialised:
-            para_compiler.init_logging_session(
+        if not RUNTIME_COMPILER.log_initialised:
+            RUNTIME_COMPILER.init_logging_session(
                 log,
                 level=logging.DEBUG if debug else logging.INFO,
             )
@@ -462,7 +404,7 @@ def parac_compile(
         # the case a prompt will appear asking the user to either answer yes
         # or no to overwriting it. If it's false a new directory with the same
         # name but a number added will be created e.g. build_2
-        build_path, dist_path = run_output_dir_validation(
+        build_path, dist_path = cli_run_output_dir_validation(
             overwrite_build,
             overwrite_dist
         )
@@ -470,7 +412,7 @@ def parac_compile(
         # Creates a CompilationProcess which represents a process that can
         # be finished but does not need to be finished
         p: ProgramCompilationProcess = abortable(
-            create_process,
+            cli_create_process,
             step="Setup",
             reraise=True
         )(
@@ -481,7 +423,7 @@ def parac_compile(
             dist_path
         )
         # Running the process with additional formatting and logging
-        return asyncio.run(run_process_with_logging(p))
+        return asyncio.run(cli_run_process_with_logging(p))
 
     @staticmethod
     @abortable(reraise=True)
@@ -518,8 +460,8 @@ def parac_syntax_check(
             debug: bool
     ):
         """ Runs a syntax check on the specified file (imports excluded) """
-        if not para_compiler.log_initialised:
-            para_compiler.init_logging_session(
+        if not RUNTIME_COMPILER.log_initialised:
+            RUNTIME_COMPILER.init_logging_session(
                 log,
                 level=logging.DEBUG if debug else logging.INFO,
                 banner_name="Syntax Check"
@@ -530,8 +472,8 @@ def parac_syntax_check(
         # Exception won't be reraised and directly logged to the console
         result = asyncio.run(p.validate_syntax(enable_out=True))
 
-        errors = para_compiler.stream_handler.errors
-        warnings = para_compiler.stream_handler.warnings
+        errors = RUNTIME_COMPILER.stream_handler.errors
+        warnings = RUNTIME_COMPILER.stream_handler.warnings
         if result is True:
             print_result_banner("Syntax Check")
             logger.info(
diff --git a/src/parac_cli/utils.py b/src/parac_cli/utils.py
new file mode 100644
index 00000000..0dd93154
--- /dev/null
+++ b/src/parac_cli/utils.py
@@ -0,0 +1,105 @@
+# coding=utf-8
+""" Utility for the parac_cli module """
+import os
+import shutil
+from os import PathLike
+from pathlib import Path
+from typing import Union, Tuple
+
+from parac import DEFAULT_BUILD_PATH, DEFAULT_DIST_PATH, UserInputError
+from parac.util import abortable, decode_if_bytes
+from parac.logging import get_rich_console as console
+
+__all__ = [
+    "cli_err_dir_already_exists",
+    "cli_run_output_dir_validation",
+    "cli_check_destination",
+    "cli_resolve_path"
+]
+
+
+def cli_resolve_path(path: Union[bytes, str, Path, PathLike]) -> str:
+    """
+    If the path is a pathlib.Path it will resolve it, including all symlinks
+    and return it as a string, else it will be passed through decode_if_bytes,
+    made to a pathlib.Path to resolve all symlinks and then returned as a
+    string
+
+    :raise UserInputError: If the inserted path can not be resolved due to an
+    invalid format
+    """
+    if str(path.strip()) == "":
+        raise UserInputError("Path can not be empty")
+    if type(path) in (bytes, str, PathLike):
+        try:
+            path = Path(decode_if_bytes(path))
+        # pathlib.Path raised error -> Invalid path
+        except Exception as e:
+            raise UserInputError("Path is in an invalid format") from e
+    return str(path.resolve())
+
+
+@abortable(step="Validating Output", reraise=True)
+def cli_err_dir_already_exists(folder: Union[str, PathLike]) -> bool:
+    """ Asks the user whether the build folder should be overwritten """
+    _input = console().input(
+        f"[bright_yellow] > [bright_white]The {folder} "
+        "folder already exists. Overwrite data? (y\\N): "
+    ).lower() == 'y'
+    return _input
+
+
+def cli_check_destination(
+        output_type: str,
+        default_path: Union[str, PathLike],
+        overwrite: bool
+) -> str:
+    """
+    Validates the destination and checks whether the specified output
+    folder is available. If the folder already exists it will show a prompt
+    to the user what should be done about the existing folder.
+
+    :returns: The path to the folder
+    """
+    output = default_path
+    if not os.path.exists(default_path):
+        os.mkdir(default_path)
+    elif len(os.listdir(default_path)) > 0:
+        # If the overwrite is set to False then a prompt will appear
+        if overwrite is False:
+            overwrite = cli_err_dir_already_exists(output_type)
+
+        if overwrite:
+            shutil.rmtree(default_path)
+            os.mkdir(default_path)
+        else:
+            counter = 2
+            while os.path.exists(f"{os.getcwd()}/{output_type}_{counter}"):
+                counter += 1
+            output = f"{os.getcwd()}/{output_type}_{counter}"
+            os.mkdir(output)
+    return output
+
+
+def cli_run_output_dir_validation(
+        overwrite_build: bool,
+        overwrite_dist: bool
+) -> Tuple[str, str]:
+    """ Validates whether the output folder /build/ and /dist/ can be used
+
+    :param overwrite_build: If set to True if a build folder already exists
+    it will be deleted and overwritten
+    :param overwrite_dist: If set to True if a dist folder already exists
+    it will be deleted and overwritten
+    """
+    build_path = cli_check_destination(
+        "build",
+        DEFAULT_BUILD_PATH,
+        overwrite_build
+    )
+    dist_path = cli_check_destination(
+        "dist",
+        DEFAULT_DIST_PATH,
+        overwrite_dist
+    )
+    return build_path, dist_path
diff --git a/src/setup.py b/src/setup.py
index afc6cc12..e545796c 100644
--- a/src/setup.py
+++ b/src/setup.py
@@ -1,7 +1,6 @@
 # coding=utf-8
 """ Setup file for the entire project """
 import os
-
 import setuptools
 from pathlib import Path
 
@@ -16,7 +15,7 @@
 
 setuptools.setup(
     name="parac",
-    version="0.1.dev1",
+    version="0.1.dev2",
     author="Luna Klatzer",
     author_email="luna.klatzer@gmail.com",
     maintainer="Luna Klatzer",


Test patch:
diff --git a/src/pytest/conftest.py b/src/pytest/conftest.py
index 23bf84f3..780d4f16 100644
--- a/src/pytest/conftest.py
+++ b/src/pytest/conftest.py
@@ -5,7 +5,7 @@
 import logging
 import parac
 
-parac.compiler.para_compiler.init_logging_session(
+parac.RUNTIME_COMPILER.init_logging_session(
     level=logging.DEBUG, print_banner=False
 )
 
diff --git a/src/pytest/tests/compiler/test_parse.py b/src/pytest/tests/compiler/test_parse.py
index b9c7d389..22236c35 100644
--- a/src/pytest/tests/compiler/test_parse.py
+++ b/src/pytest/tests/compiler/test_parse.py
@@ -5,16 +5,16 @@
 from typing import List
 import asyncio
 
-from parac import SEPARATOR as SEP
+from parac import SEPARATOR as SEP, RUNTIME_COMPILER
 from parac.logging import set_avoid_print_banner_overwrite
-from parac.compiler import ParacCompiler, para_compiler, BasicProcess
+from parac.compiler import ParacCompiler, BasicProcess
 
 from .. import reset_input
 
 logger = logging.getLogger('compiler')
 
 compiler = ParacCompiler()
-para_compiler.init_logging_session(
+RUNTIME_COMPILER.init_logging_session(
     level=logging.DEBUG, print_banner=False
 )
 set_avoid_print_banner_overwrite(True)
diff --git a/src/pytest/tests/compiler/test_setup.py b/src/pytest/tests/compiler/test_setup.py
index 763434a1..3a1293f0 100644
--- a/src/pytest/tests/compiler/test_setup.py
+++ b/src/pytest/tests/compiler/test_setup.py
@@ -5,10 +5,10 @@
 import os
 
 from parac import (FileNotFoundError as ParaFileNotFoundError,
-                   SEPARATOR as SEP)
+                   SEPARATOR as SEP, UserInputError)
 from parac.logging import set_avoid_print_banner_overwrite
-from parac.compiler import (ParacCompiler, create_process,
-                            run_output_dir_validation)
+from parac.compiler import ParacCompiler
+from parac_cli import cli_run_output_dir_validation, cli_create_process
 
 from .. import (add_folder, overwrite_builtin_input, reset_input,
                 create_test_file)
@@ -33,14 +33,14 @@ def test_build_exists_setup(self):
         create_test_file("build", "example.txt")
 
         overwrite_builtin_input('True')
-        run_output_dir_validation(False, True)
+        cli_run_output_dir_validation(False, True)
         assert not os.path.exists("./build_2/example.txt")
         assert os.path.exists("./build/example.txt")
 
         create_test_file("build", "example.txt")
 
         overwrite_builtin_input('False')
-        run_output_dir_validation(True, True)
+        cli_run_output_dir_validation(True, True)
         assert not os.path.exists("./build/example.txt")
         add_folder("build")
 
@@ -49,13 +49,13 @@ def test_dist_exists_setup(self):
         create_test_file("dist", "example.txt")
 
         overwrite_builtin_input('True')  # Overwrite data -> True
-        run_output_dir_validation(True, False)
+        cli_run_output_dir_validation(True, False)
         assert not os.path.exists("./dist_2/example.txt")
         assert os.path.exists("./dist/example.txt")
 
         create_test_file("dist", "example.txt")
         overwrite_builtin_input('False')  # Overwrite data -> False
-        run_output_dir_validation(True, True)
+        cli_run_output_dir_validation(True, True)
         assert not os.path.exists("./dist/example.txt")
         add_folder("dist")
 
@@ -63,7 +63,7 @@ def test_simple_setup_compilation_process(self):
         b_path = add_folder("build")
         d_path = add_folder("dist")
 
-        p = create_process(
+        p = cli_create_process(
             main_file_path, ENCODING, LOG_PATH, b_path, d_path
         )
 
@@ -72,20 +72,43 @@ def test_simple_setup_compilation_process(self):
 
     @pytest.mark.parametrize(
         "path", [
-            "not_existing.para", "not_existing", " ", ""
+            "not_existing.para", "not_existing"
         ]
     )
-    def test_wrong_path_compilation_process(self, path: str):
+    def test_wrong_path_compilation_process_1(self, path: str):
         b_path = add_folder("build")
         d_path = add_folder("dist")
         try:
-            create_process(
-                path, ENCODING, LOG_PATH, b_path, d_path
+            p = cli_create_process(
+                file=path,
+                encoding=ENCODING,
+                log_path=LOG_PATH,
+                build_path=b_path,
+                dist_path=d_path
             )
+            assert False
         except ParaFileNotFoundError as e:
             ...
-        else:
+
+    @pytest.mark.parametrize(
+        "path", [
+            "", "", "ddf $  &/`='|.*"
+        ]
+    )
+    def test_wrong_path_compilation_process_2(self, path: str):
+        b_path = add_folder("build")
+        d_path = add_folder("dist")
+        try:
+            p = cli_create_process(
+                file=path,
+                encoding=ENCODING,
+                log_path=LOG_PATH,
+                build_path=b_path,
+                dist_path=d_path
+            )
             assert False
+        except UserInputError:
+            ...
 
     @pytest.mark.parametrize(
         "expected,input_str,line_ending", [
diff --git a/src/pytest/tests/preprocessor/test_parse.py b/src/pytest/tests/preprocessor/test_parse.py
index 5b8f1c60..4fda34ac 100644
--- a/src/pytest/tests/preprocessor/test_parse.py
+++ b/src/pytest/tests/preprocessor/test_parse.py
@@ -3,18 +3,16 @@
 import logging
 import os
 from typing import List
-
 import asyncio
 
-from parac.compiler import (ParacCompiler, ProgramCompilationProcess,
-                            para_compiler)
-from parac import SEPARATOR as SEP
+from parac.compiler import (ParacCompiler, ProgramCompilationProcess)
+from parac import SEPARATOR as SEP, RUNTIME_COMPILER
 from parac.logging import set_avoid_print_banner_overwrite
 
 from .. import add_folder, remove_folder, reset_input
 
 compiler = ParacCompiler()
-para_compiler.init_logging_session(
+RUNTIME_COMPILER.init_logging_session(
     level=logging.DEBUG, print_banner=False
 )
 set_avoid_print_banner_overwrite(True)