diff --git a/src/dotenv/__init__.py b/src/dotenv/__init__.py index 1d7a4233..3512d101 100644 --- a/src/dotenv/__init__.py +++ b/src/dotenv/__init__.py @@ -4,14 +4,18 @@ unset_key) -def load_ipython_extension(ipython): - # type: (Any) -> None +def load_ipython_extension(ipython: Any) -> None: from .ipython import load_ipython_extension load_ipython_extension(ipython) -def get_cli_string(path=None, action=None, key=None, value=None, quote=None): - # type: (Optional[str], Optional[str], Optional[str], Optional[str], Optional[str]) -> str +def get_cli_string( + path: Optional[str] = None, + action: Optional[str] = None, + key: Optional[str] = None, + value: Optional[str] = None, + quote: Optional[str] = None, +): """Returns a string suitable for running as a shell script. Useful for converting a arguments passed to a fabric task diff --git a/src/dotenv/cli.py b/src/dotenv/cli.py index bd593a66..b7ae24af 100644 --- a/src/dotenv/cli.py +++ b/src/dotenv/cli.py @@ -26,8 +26,7 @@ help="Whether to write the dot file as an executable bash script.") @click.version_option(version=__version__) @click.pass_context -def cli(ctx, file, quote, export): - # type: (click.Context, Any, Any, Any) -> None +def cli(ctx: click.Context, file: Any, quote: Any, export: Any) -> None: '''This script is used to set, get or unset values from a .env file.''' ctx.obj = {} ctx.obj['QUOTE'] = quote @@ -37,8 +36,7 @@ def cli(ctx, file, quote, export): @cli.command() @click.pass_context -def list(ctx): - # type: (click.Context) -> None +def list(ctx: click.Context) -> None: '''Display all the stored key/value.''' file = ctx.obj['FILE'] if not os.path.isfile(file): @@ -55,8 +53,7 @@ def list(ctx): @click.pass_context @click.argument('key', required=True) @click.argument('value', required=True) -def set(ctx, key, value): - # type: (click.Context, Any, Any) -> None +def set(ctx: click.Context, key: Any, value: Any) -> None: '''Store the given key/value.''' file = ctx.obj['FILE'] quote = ctx.obj['QUOTE'] @@ -71,8 +68,7 @@ def set(ctx, key, value): @cli.command() @click.pass_context @click.argument('key', required=True) -def get(ctx, key): - # type: (click.Context, Any) -> None +def get(ctx: click.Context, key: Any) -> None: '''Retrieve the value for the given key.''' file = ctx.obj['FILE'] if not os.path.isfile(file): @@ -90,8 +86,7 @@ def get(ctx, key): @cli.command() @click.pass_context @click.argument('key', required=True) -def unset(ctx, key): - # type: (click.Context, Any) -> None +def unset(ctx: click.Context, key: Any) -> None: '''Removes the given key.''' file = ctx.obj['FILE'] quote = ctx.obj['QUOTE'] @@ -110,8 +105,7 @@ def unset(ctx, key): help="Override variables from the environment file with those from the .env file.", ) @click.argument('commandline', nargs=-1, type=click.UNPROCESSED) -def run(ctx, override, commandline): - # type: (click.Context, bool, List[str]) -> None +def run(ctx: click.Context, override: bool, commandline: List[str]) -> None: """Run command with environment variables present.""" file = ctx.obj['FILE'] if not os.path.isfile(file): @@ -132,8 +126,7 @@ def run(ctx, override, commandline): exit(ret) -def run_command(command, env): - # type: (List[str], Dict[str, str]) -> int +def run_command(command: List[str], env: Dict[str, str]) -> int: """Run command in sub process. Runs the command in a sub process with the variables from `env` diff --git a/src/dotenv/main.py b/src/dotenv/main.py index 9e6cb437..e4e140f3 100644 --- a/src/dotenv/main.py +++ b/src/dotenv/main.py @@ -20,8 +20,7 @@ _PathLike = Text -def with_warn_for_invalid_lines(mappings): - # type: (Iterator[Binding]) -> Iterator[Binding] +def with_warn_for_invalid_lines(mappings: Iterator[Binding]) -> Iterator[Binding]: for mapping in mappings: if mapping.error: logger.warning( @@ -32,9 +31,14 @@ def with_warn_for_invalid_lines(mappings): class DotEnv(): - - def __init__(self, dotenv_path, verbose=False, encoding=None, interpolate=True, override=True): - # type: (Union[Text, _PathLike, io.StringIO], bool, Union[None, Text], bool, bool) -> None + def __init__( + self, + dotenv_path: Union[Text, _PathLike, io.StringIO], + verbose: bool = False, + encoding: Union[None, Text] = None, + interpolate: bool = True, + override: bool = True, + ) -> None: self.dotenv_path = dotenv_path # type: Union[Text,_PathLike, io.StringIO] self._dict = None # type: Optional[Dict[Text, Optional[Text]]] self.verbose = verbose # type: bool @@ -43,8 +47,7 @@ def __init__(self, dotenv_path, verbose=False, encoding=None, interpolate=True, self.override = override # type: bool @contextmanager - def _get_stream(self): - # type: () -> Iterator[IO[Text]] + def _get_stream(self) -> Iterator[IO[Text]]: if isinstance(self.dotenv_path, io.StringIO): yield self.dotenv_path elif os.path.isfile(self.dotenv_path): @@ -55,8 +58,7 @@ def _get_stream(self): logger.info("Python-dotenv could not find configuration file %s.", self.dotenv_path or '.env') yield io.StringIO('') - def dict(self): - # type: () -> Dict[Text, Optional[Text]] + def dict(self) -> Dict[Text, Optional[Text]]: """Return dotenv as dict""" if self._dict: return self._dict @@ -70,15 +72,13 @@ def dict(self): return self._dict - def parse(self): - # type: () -> Iterator[Tuple[Text, Optional[Text]]] + def parse(self) -> Iterator[Tuple[Text, Optional[Text]]]: with self._get_stream() as stream: for mapping in with_warn_for_invalid_lines(parse_stream(stream)): if mapping.key is not None: yield mapping.key, mapping.value - def set_as_environment_variables(self): - # type: () -> bool + def set_as_environment_variables(self) -> bool: """ Load the current dotenv as system environment variable. """ @@ -90,8 +90,7 @@ def set_as_environment_variables(self): return True - def get(self, key): - # type: (Text) -> Optional[Text] + def get(self, key: Text) -> Optional[Text]: """ """ data = self.dict() @@ -105,8 +104,7 @@ def get(self, key): return None -def get_key(dotenv_path, key_to_get): - # type: (Union[Text, _PathLike], Text) -> Optional[Text] +def get_key(dotenv_path: Union[Text, _PathLike], key_to_get: Text) -> Optional[Text]: """ Gets the value of a given key from the given .env @@ -116,8 +114,7 @@ def get_key(dotenv_path, key_to_get): @contextmanager -def rewrite(path): - # type: (_PathLike) -> Iterator[Tuple[IO[Text], IO[Text]]] +def rewrite(path: _PathLike) -> Iterator[Tuple[IO[Text], IO[Text]]]: try: if not os.path.isfile(path): with io.open(path, "w+") as source: @@ -133,8 +130,13 @@ def rewrite(path): shutil.move(dest.name, path) -def set_key(dotenv_path, key_to_set, value_to_set, quote_mode="always", export=False): - # type: (_PathLike, Text, Text, Text, bool) -> Tuple[Optional[bool], Text, Text] +def set_key( + dotenv_path: _PathLike, + key_to_set: Text, + value_to_set: Text, + quote_mode: Text = "always", + export: bool = False, +) -> Tuple[Optional[bool], Text, Text]: """ Adds or Updates a key/value to the given .env @@ -172,8 +174,11 @@ def set_key(dotenv_path, key_to_set, value_to_set, quote_mode="always", export=F return True, key_to_set, value_to_set -def unset_key(dotenv_path, key_to_unset, quote_mode="always"): - # type: (_PathLike, Text, Text) -> Tuple[Optional[bool], Text] +def unset_key( + dotenv_path: _PathLike, + key_to_unset: Text, + quote_mode: Text = "always", +) -> Tuple[Optional[bool], Text]: """ Removes a given key from the given .env @@ -199,9 +204,10 @@ def unset_key(dotenv_path, key_to_unset, quote_mode="always"): return removed, key_to_unset -def resolve_variables(values, override): - # type: (Iterable[Tuple[Text, Optional[Text]]], bool) -> Mapping[Text, Optional[Text]] - +def resolve_variables( + values: Iterable[Tuple[Text, Optional[Text]]], + override: bool, +) -> Mapping[Text, Optional[Text]]: new_values = {} # type: Dict[Text, Optional[Text]] for (name, value) in values: @@ -223,8 +229,7 @@ def resolve_variables(values, override): return new_values -def _walk_to_root(path): - # type: (Text) -> Iterator[Text] +def _walk_to_root(path: Text) -> Iterator[Text]: """ Yield directories starting from the given directory up to the root """ @@ -242,8 +247,11 @@ def _walk_to_root(path): last_dir, current_dir = current_dir, parent_dir -def find_dotenv(filename='.env', raise_error_if_not_found=False, usecwd=False): - # type: (Text, bool, bool) -> Text +def find_dotenv( + filename: Text = '.env', + raise_error_if_not_found: bool = False, + usecwd: bool = False, +) -> Text: """ Search in increasingly higher folders for the given file @@ -281,14 +289,13 @@ def _is_interactive(): def load_dotenv( - dotenv_path=None, - stream=None, - verbose=False, - override=False, - interpolate=True, - encoding="utf-8", -): - # type: (Union[Text, _PathLike, None], Optional[io.StringIO], bool, bool, bool, Optional[Text]) -> bool # noqa + dotenv_path: Union[Text, _PathLike, None] = None, + stream: Optional[io.StringIO] = None, + verbose: bool = False, + override: bool = False, + interpolate: bool = True, + encoding: Optional[Text] = "utf-8", +) -> bool: """Parse a .env file and then load all the variables found as environment variables. - *dotenv_path*: absolute or relative path to .env file. @@ -313,13 +320,12 @@ def load_dotenv( def dotenv_values( - dotenv_path=None, - stream=None, - verbose=False, - interpolate=True, - encoding="utf-8", -): - # type: (Union[Text, _PathLike, None], Optional[io.StringIO], bool, bool, Optional[Text]) -> Dict[Text, Optional[Text]] # noqa: E501 + dotenv_path: Union[Text, _PathLike, None] = None, + stream: Optional[io.StringIO] = None, + verbose: bool = False, + interpolate: bool = True, + encoding: Optional[Text] = "utf-8", +) -> Dict[Text, Optional[Text]]: """ Parse a .env file and return its content as a dict. diff --git a/src/dotenv/parser.py b/src/dotenv/parser.py index 0d9b9d3f..8a976c51 100644 --- a/src/dotenv/parser.py +++ b/src/dotenv/parser.py @@ -4,8 +4,7 @@ Pattern, Sequence, Text, Tuple) -def make_regex(string, extra_flags=0): - # type: (str, int) -> Pattern[Text] +def make_regex(string: str, extra_flags: int = 0) -> Pattern[Text]: return re.compile(string, re.UNICODE | extra_flags) @@ -46,23 +45,19 @@ def make_regex(string, extra_flags=0): class Position: - def __init__(self, chars, line): - # type: (int, int) -> None + def __init__(self, chars: int, line: int) -> None: self.chars = chars self.line = line @classmethod - def start(cls): - # type: () -> Position + def start(cls) -> "Position": return cls(chars=0, line=1) - def set(self, other): - # type: (Position) -> None + def set(self, other: "Position") -> None: self.chars = other.chars self.line = other.line - def advance(self, string): - # type: (Text) -> None + def advance(self, string: Text) -> None: self.chars += len(string) self.line += len(re.findall(_newline, string)) @@ -72,41 +67,34 @@ class Error(Exception): class Reader: - def __init__(self, stream): - # type: (IO[Text]) -> None + def __init__(self, stream: IO[Text]) -> None: self.string = stream.read() self.position = Position.start() self.mark = Position.start() - def has_next(self): - # type: () -> bool + def has_next(self) -> bool: return self.position.chars < len(self.string) - def set_mark(self): - # type: () -> None + def set_mark(self) -> None: self.mark.set(self.position) - def get_marked(self): - # type: () -> Original + def get_marked(self) -> Original: return Original( string=self.string[self.mark.chars:self.position.chars], line=self.mark.line, ) - def peek(self, count): - # type: (int) -> Text + def peek(self, count: int) -> Text: return self.string[self.position.chars:self.position.chars + count] - def read(self, count): - # type: (int) -> Text + def read(self, count: int) -> Text: result = self.string[self.position.chars:self.position.chars + count] if len(result) < count: raise Error("read: End of string") self.position.advance(result) return result - def read_regex(self, regex): - # type: (Pattern[Text]) -> Sequence[Text] + def read_regex(self, regex: Pattern[Text]) -> Sequence[Text]: match = regex.match(self.string, self.position.chars) if match is None: raise Error("read_regex: Pattern not found") @@ -114,17 +102,14 @@ def read_regex(self, regex): return match.groups() -def decode_escapes(regex, string): - # type: (Pattern[Text], Text) -> Text - def decode_match(match): - # type: (Match[Text]) -> Text +def decode_escapes(regex: Pattern[Text], string: Text) -> Text: + def decode_match(match: Match[Text]) -> Text: return codecs.decode(match.group(0), 'unicode-escape') # type: ignore return regex.sub(decode_match, string) -def parse_key(reader): - # type: (Reader) -> Optional[Text] +def parse_key(reader: Reader) -> Optional[Text]: char = reader.peek(1) if char == "#": return None @@ -135,14 +120,12 @@ def parse_key(reader): return key -def parse_unquoted_value(reader): - # type: (Reader) -> Text +def parse_unquoted_value(reader: Reader) -> Text: (part,) = reader.read_regex(_unquoted_value) return re.sub(r"\s+#.*", "", part).rstrip() -def parse_value(reader): - # type: (Reader) -> Text +def parse_value(reader: Reader) -> Text: char = reader.peek(1) if char == u"'": (value,) = reader.read_regex(_single_quoted_value) @@ -156,8 +139,7 @@ def parse_value(reader): return parse_unquoted_value(reader) -def parse_binding(reader): - # type: (Reader) -> Binding +def parse_binding(reader: Reader) -> Binding: reader.set_mark() try: reader.read_regex(_multiline_whitespace) @@ -194,8 +176,7 @@ def parse_binding(reader): ) -def parse_stream(stream): - # type: (IO[Text]) -> Iterator[Binding] +def parse_stream(stream: IO[Text]) -> Iterator[Binding]: reader = Reader(stream) while reader.has_next(): yield parse_binding(reader) diff --git a/src/dotenv/variables.py b/src/dotenv/variables.py index 83fe11c1..bddd07e1 100644 --- a/src/dotenv/variables.py +++ b/src/dotenv/variables.py @@ -18,71 +18,58 @@ class Atom(): __metaclass__ = ABCMeta - def __ne__(self, other): - # type: (object) -> bool + def __ne__(self, other: object) -> bool: result = self.__eq__(other) if result is NotImplemented: return NotImplemented return not result - def resolve(self, env): - # type: (Mapping[Text, Optional[Text]]) -> Text + def resolve(self, env: Mapping[Text, Optional[Text]]) -> Text: raise NotImplementedError class Literal(Atom): - def __init__(self, value): - # type: (Text) -> None + def __init__(self, value: Text) -> None: self.value = value - def __repr__(self): - # type: () -> str + def __repr__(self) -> str: return "Literal(value={})".format(self.value) - def __eq__(self, other): - # type: (object) -> bool + def __eq__(self, other: object) -> bool: if not isinstance(other, self.__class__): return NotImplemented return self.value == other.value - def __hash__(self): - # type: () -> int + def __hash__(self) -> int: return hash((self.__class__, self.value)) - def resolve(self, env): - # type: (Mapping[Text, Optional[Text]]) -> Text + def resolve(self, env: Mapping[Text, Optional[Text]]) -> Text: return self.value class Variable(Atom): - def __init__(self, name, default): - # type: (Text, Optional[Text]) -> None + def __init__(self, name: Text, default: Optional[Text]) -> None: self.name = name self.default = default - def __repr__(self): - # type: () -> str + def __repr__(self) -> str: return "Variable(name={}, default={})".format(self.name, self.default) - def __eq__(self, other): - # type: (object) -> bool + def __eq__(self, other: object) -> bool: if not isinstance(other, self.__class__): return NotImplemented return (self.name, self.default) == (other.name, other.default) - def __hash__(self): - # type: () -> int + def __hash__(self) -> int: return hash((self.__class__, self.name, self.default)) - def resolve(self, env): - # type: (Mapping[Text, Optional[Text]]) -> Text + def resolve(self, env: Mapping[Text, Optional[Text]]) -> Text: default = self.default if self.default is not None else "" result = env.get(self.name, default) return result if result is not None else "" -def parse_variables(value): - # type: (Text) -> Iterator[Atom] +def parse_variables(value: Text) -> Iterator[Atom]: cursor = 0 for match in _posix_variable.finditer(value):