-
-
Notifications
You must be signed in to change notification settings - Fork 689
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
A utility module has been created to work with android external storage files #2910
base: main
Are you sure you want to change the base?
Changes from 14 commits
024dbb6
23684ae
9b7af36
c4e245f
e46c5df
0d66bbf
f908fef
f122867
132f093
840bbbb
31bd71d
ba66a0c
4bc9ed0
a89e3a9
cb321c1
3618da8
653fdfc
e6d39e0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
import abc | ||
import asyncio as aio | ||
import io | ||
|
||
from java.io import BufferedReader, InputStreamReader, OutputStreamWriter | ||
from java.util import Objects | ||
|
||
|
||
class HandlerFileDialog(abc.ABC): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not clear to me why this is in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was just afraid to change something in the main file. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok... but it's a dialog class. It should be with dialogs, not with utilities for file handling. |
||
"""An abstract class that handles file manager calls""" | ||
|
||
def __init__(self, parent, app_toga): | ||
self.parent = parent | ||
self.app = app_toga._impl.app | ||
self.mActive = app_toga._impl.app.native | ||
|
||
@abc.abstractmethod | ||
def show(self): | ||
"""Запуск менеджера""" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As noted previously, we need the code to be documented in English. |
||
pass | ||
|
||
|
||
class BasePath(io.TextIOBase, abc.ABC): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems like something that should be a core-level utility, not something that is Android specific. It's also unclear why an object named |
||
modes = [] | ||
|
||
def __init__(self, app, stream): | ||
self.aloop: aio.AbstractEventLoop = app.loop | ||
self._stream = stream | ||
self._buffer = None | ||
self._is_binary = False | ||
self._new_line_int = 10 | ||
self._encoding = "utf-8" | ||
|
||
def check_open(self): | ||
"""The defense mechanism on the open is that path""" | ||
if self._buffer is None: | ||
raise TypeError("File not open!") | ||
|
||
def __enter__(self): | ||
return self | ||
|
||
def __exit__(self, exc_type, exc_val, exc_tb): | ||
self.close() | ||
|
||
def close(self): | ||
self._stream.close() | ||
if self._buffer is not None: | ||
self._buffer.close() | ||
|
||
@abc.abstractmethod | ||
def open(self, mode, encoding="utf-8", new_line="\n"): | ||
"""Method for opening a file by path""" | ||
self._encoding = encoding | ||
self._new_line_int = new_line.encode(encoding)[0] | ||
if mode not in self.modes: | ||
raise ValueError( | ||
f"""invalid mode {mode}. | ||
It is allowed to use the following modes: R or RB""" | ||
) | ||
if "b" in mode: | ||
self._is_binary = True | ||
|
||
|
||
class BasePathReader(BasePath, abc.ABC): | ||
modes = ["r", "rb"] | ||
|
||
def open(self, mode="r", encoding="utf-8", new_line="\n"): | ||
"""A method for opening a file for reading along the path""" | ||
super().open(mode.lower(), encoding, new_line) | ||
input_stream_reader = InputStreamReader(Objects.requireNonNull(self._stream)) | ||
self._buffer = BufferedReader(input_stream_reader) | ||
|
||
@abc.abstractmethod | ||
def read(self, size=0): | ||
pass | ||
|
||
@abc.abstractmethod | ||
def readline(self, size=0): | ||
pass | ||
|
||
@abc.abstractmethod | ||
def aread(self, size=0): | ||
pass | ||
|
||
@abc.abstractmethod | ||
def areadline(self, size=0): | ||
pass | ||
|
||
|
||
class PathReader(BasePathReader): | ||
"""A phalloid object for reading. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A what? Are you sure this is the word you mean? |
||
Reads the contents of the Android external storage file""" | ||
|
||
def readline(self, size: int = 0) -> str | bytes: | ||
"""A function for reading lines from a file | ||
:param size: the number of rows to be counted. | ||
(Currently not implemented, added for future implementation) | ||
:return: Data type str[bytes] | ||
(Depends on whether the flag and was passed) or the list of str[bytes] | ||
""" | ||
self.check_open() | ||
res = bytearray() | ||
counter = size if size else "-1" | ||
while counter: | ||
resp: int = self._buffer.read() | ||
if resp == -1: | ||
break | ||
if resp == self._new_line_int: | ||
break | ||
res.append(resp) | ||
if isinstance(counter, int): | ||
counter -= 1 | ||
if self._is_binary: | ||
return bytes(res) | ||
return res.decode(self._encoding) | ||
|
||
def read(self, size: int = 0) -> bytes | str: | ||
"""A function for reading a string | ||
:param size: the number of characters to be counted. | ||
If it is equal to 0, the entire file will be read | ||
:return: Data type str[bytes] | ||
(depends on whether the 'b' flag was passed)""" | ||
self.check_open() | ||
res = bytearray() | ||
counter = size if size else "-1" | ||
while counter: | ||
resp: int = self._buffer.read() | ||
if resp == -1: | ||
break | ||
res.append(resp) | ||
if isinstance(counter, int): | ||
counter -= 1 | ||
if self._is_binary: | ||
return bytes(res) | ||
return res.decode(self._encoding) | ||
|
||
async def aread(self, size=0): | ||
"""A function for reading a string | ||
:param size: the number of characters to be counted. | ||
If it is equal to 0, the entire file will be read | ||
:return: Data type str[bytes] | ||
(depends on whether the 'b' flag was passed)""" | ||
self.check_open() | ||
res = bytearray() | ||
counter = size if size else "-1" | ||
while counter: | ||
resp: int = await self.aloop.run_in_executor(None, self._buffer.read) | ||
if resp == -1: | ||
break | ||
res.append(resp) | ||
if isinstance(counter, int): | ||
counter -= 1 | ||
if self._is_binary: | ||
return bytes(res) | ||
return res.decode(self._encoding) | ||
|
||
def areadline(self, size=0): | ||
return NotImplemented | ||
|
||
|
||
class BasePathWriter(BasePath, abc.ABC): | ||
modes = ["w", "wb"] | ||
|
||
def open(self, mode="r", encoding="utf-8", new_line="\n"): | ||
"""A method for opening a file for writing along the path""" | ||
super().open(mode.lower(), encoding, new_line) | ||
self._buffer = OutputStreamWriter(self._stream) | ||
|
||
def _convertion_type_data(self, data): | ||
if isinstance(data, bytes) and not self._is_binary: | ||
return data.decode(self._encoding) | ||
if isinstance(data, str) and self._is_binary: | ||
return data.encode(self._encoding) | ||
return data | ||
|
||
@abc.abstractmethod | ||
def write(self, text: str | bytes): | ||
data = self._convertion_type_data(text) | ||
self._buffer.write(data) | ||
|
||
@abc.abstractmethod | ||
def awrite(self, text): | ||
pass | ||
|
||
|
||
class PathWriter(BasePathReader): | ||
pass |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
class Singtools(type): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't work out what this collection of utilities is trying to achieve, or why it is almost duplicated between the Android and Winforms backends. |
||
_instance = None | ||
|
||
def __call__(cls, *args, **kwargs): | ||
if cls._instance is None: | ||
cls._instance = super().__call__(cls, *args, **kwargs) | ||
return cls._instance | ||
|
||
|
||
class ManagerGlobal(metaclass=Singtools): | ||
def __init__(self, app=None): | ||
self._app = app | ||
|
||
|
||
class BaseManager(metaclass=Singtools): | ||
def getApp(self): | ||
return ManagerGlobal()._app | ||
|
||
|
||
class ManagerFile(BaseManager): | ||
def open(self, path, mode="r", encoding="utf-8", new_line="\n"): | ||
pass | ||
|
||
def delete(self, path): | ||
pass | ||
|
||
|
||
class ManagerFolder(BaseManager): | ||
pass |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
Android implementation of OpenFileDialog | ||
|
||
Adding a utility module for reading from external storage files | ||
|
||
Adding singleton for file management on Android/Windows for API communication | ||
|
||
Support with Python 3.10+ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The change note isn't a commit message. It's a description of the new feature as it would appear in the release notes. To that end, this shouldn't be a |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import os | ||
|
||
|
||
class Singtools(type): | ||
_instance = None | ||
|
||
def __call__(cls, *args, **kwargs): | ||
if cls._instance is None: | ||
cls._instance = super().__call__(cls, *args, **kwargs) | ||
return cls._instance | ||
|
||
|
||
class ManagerGlobal(metaclass=Singtools): | ||
def __init__(self, app=None): | ||
self._app = app | ||
|
||
|
||
class BaseManager(metaclass=Singtools): | ||
def getApp(self): | ||
return ManagerGlobal()._app | ||
|
||
|
||
class ManagerFile(BaseManager): | ||
|
||
def open(self, path, mode="r", encoding="utf-8", new_line="\n", external=True): | ||
return open(path, mode, encoding=encoding, newline=new_line) | ||
|
||
def delete(self, path): | ||
os.remove(path) | ||
|
||
|
||
class ManagerFolder(BaseManager): | ||
pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As noted previously, there's no reason for this class to be nested.