Skip to content

Commit c4f272b

Browse files
Merge pull request #75 from oele-isis-vanderbilt/dev_branch
Bug Patch
2 parents 1661194 + 4606a1c commit c4f272b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1237
-713
lines changed

Dockerfile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,7 @@ RUN ls
1717

1818
# Install ChimeraPy
1919
RUN python3 -m pip install --upgrade pip
20-
RUN cd ChimeraPy && python3 -m pip install '.[test]'
20+
RUN cd ChimeraPy && python3 -m pip install '.[test]' && cd ..
21+
22+
# For a certain test, remove the mock
23+
RUN rm -r ChimeraPy/test/mock/test_package

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
![Logo](docs/_static/logo/chimerapy_logo_with_name_theme_blue.png)
1+
![Logo](https://user-images.githubusercontent.com/40870026/204550212-a6e1b7c2-194b-4554-ab42-f5e456c6f402.png)
22

33
[![PyPI](https://img.shields.io/pypi/v/chimerapy)](https://pypi.org/project/chimerapy/) [![Coverage Status](https://coveralls.io/repos/github/oele-isis-vanderbilt/ChimeraPy/badge.svg?branch=main)](https://coveralls.io/github/oele-isis-vanderbilt/ChimeraPy?branch=main) ![](https://img.shields.io/github/workflow/status/oele-isis-vanderbilt/ChimeraPy/Test) ![](https://img.shields.io/github/license/oele-isis-vanderbilt/ChimeraPy) ![](https://img.shields.io/badge/style-black-black)
44
* [Docs](https://oele-isis-vanderbilt.github.io/ChimeraPy)

chimerapy/__init__.py

Lines changed: 8 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,10 @@
1-
# Setup the logging for the library
2-
# References:
3-
# https://docs.python-guide.org/writing/logging/
4-
# https://stackoverflow.com/questions/13649664/how-to-use-logging-with-pythons-fileconfig-and-configure-the-logfile-filename
5-
import logging.config
1+
# Meta data
2+
__version__ = "0.0.7"
63

7-
LOGGING_CONFIG = {
8-
"version": 1,
9-
"disable_existing_loggers": True,
10-
"formatters": {
11-
"standard": {
12-
"format": "%(asctime)s [%(levelname)s] %(name)s: %(message)s",
13-
"datefmt": "%Y-%m-%d %H:%M:%S",
14-
},
15-
},
16-
"handlers": {
17-
"console": {
18-
"level": "INFO",
19-
"formatter": "standard",
20-
"class": "logging.StreamHandler",
21-
"stream": "ext://sys.stdout", # Default is stderr
22-
},
23-
"datagram": {
24-
"level": "INFO",
25-
"formatter": "standard",
26-
"class": "logging.handlers.DatagramHandler",
27-
"host": "127.0.0.1",
28-
"port": 5555,
29-
},
30-
},
31-
"loggers": {
32-
"chimerapy": {
33-
"handlers": ["console"],
34-
"level": "INFO",
35-
"propagate": True,
36-
},
37-
"subprocess": {"handlers": ["datagram"], "level": "INFO", "propagate": True},
38-
},
39-
}
4+
# Package Setup
5+
from . import _logger
406

41-
# Setup the logging configuration
42-
logging.config.dictConfig(LOGGING_CONFIG)
7+
_logger.setup()
438

449
# Interal Imports
4510
from .manager import Manager
@@ -58,3 +23,6 @@
5823

5924
# Then define the entry points
6025
from . import entry
26+
27+
# Debugging tools
28+
from ._debug import debug

chimerapy/_debug.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Built-in Import
2+
from typing import List, Optional
3+
import logging
4+
import os
5+
6+
# Internal Imports
7+
from ._logger import LOGGING_CONFIG
8+
9+
10+
def debug(loggers: Optional[List[str]] = None):
11+
12+
# Not provided, then get all
13+
if type(loggers) == type(None):
14+
loggers = [x for x in LOGGING_CONFIG["loggers"]]
15+
16+
assert loggers != None
17+
18+
# Change env variable and configurations
19+
os.environ["CHIMERAPY_DEBUG_LOGGERS"] = os.pathsep.join(loggers)
20+
for logger_name in loggers:
21+
logging.getLogger(logger_name).setLevel(logging.DEBUG)

chimerapy/_logger.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Setup the logging for the library
2+
# References:
3+
# https://docs.python-guide.org/writing/logging/
4+
# https://stackoverflow.com/questions/13649664/how-to-use-logging-with-pythons-fileconfig-and-configure-the-logfile-filename
5+
import logging.config
6+
import os
7+
8+
LOGGING_CONFIG = {
9+
"version": 1,
10+
"disable_existing_loggers": True,
11+
"formatters": {
12+
"standard": {
13+
"format": "%(asctime)s [%(levelname)s] %(name)s: %(message)s",
14+
"datefmt": "%Y-%m-%d %H:%M:%S",
15+
},
16+
},
17+
"handlers": {
18+
"console": {
19+
"level": "INFO",
20+
"formatter": "standard",
21+
"class": "logging.StreamHandler",
22+
"stream": "ext://sys.stdout", # Default is stderr
23+
},
24+
"datagram": {
25+
"level": "DEBUG",
26+
"formatter": "standard",
27+
"class": "logging.handlers.DatagramHandler",
28+
"host": "127.0.0.1",
29+
"port": 5555,
30+
},
31+
},
32+
"loggers": {
33+
"chimerapy": {
34+
"handlers": ["console"],
35+
"level": "INFO",
36+
"propagate": True,
37+
},
38+
"chimerapy-networking": {
39+
"handlers": ["console"],
40+
"level": "INFO",
41+
"propagate": True,
42+
},
43+
"chimerapy-subprocess": {
44+
"handlers": ["datagram"],
45+
"level": "INFO",
46+
"propagate": True,
47+
},
48+
},
49+
}
50+
51+
# Setup the logging configuration
52+
def setup():
53+
54+
# Setting up the configureation
55+
logging.config.dictConfig(LOGGING_CONFIG)
56+
57+
58+
def getLogger(name: str):
59+
60+
# Get the logging
61+
logger = logging.getLogger(name)
62+
63+
# Ensure that the configuration is set
64+
debug_loggers = os.environ.get("CHIMERAPY_DEBUG_LOGGERS", "").split(os.pathsep)
65+
if name in debug_loggers:
66+
logger.setLevel(logging.DEBUG)
67+
68+
return logger

chimerapy/client.py

Lines changed: 51 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Built-in Imports
2-
from typing import Literal, Callable, Dict
2+
from typing import Literal, Callable, Dict, Any
33
import os
44
import logging
55
import collections
@@ -18,10 +18,12 @@
1818
# Third-party Imports
1919

2020
# Internal Imports
21+
from . import socket_handling as sh
2122
from .utils import create_payload, decode_payload
2223
from . import enums
24+
from . import _logger
2325

24-
logger = logging.getLogger("chimerapy")
26+
logger = _logger.getLogger("chimerapy-networking")
2527

2628

2729
class Client(threading.Thread):
@@ -55,6 +57,8 @@ def __init__(
5557

5658
# Creating tempfolder to host items
5759
self.tempfolder = pathlib.Path(tempfile.mkdtemp())
60+
self.handlers.update({enums.FILE_TRANSFER_START: self.receive_file})
61+
self.file_transfer_records = collections.defaultdict(dict)
5862

5963
# Create the socket and connect
6064
try:
@@ -115,69 +119,32 @@ def run(self):
115119
# Continue checking for messages from client until not running
116120
while self.is_running.is_set():
117121

118-
# Check if the socket is closed
119-
if self.socket.fileno() == -1:
120-
break
121-
122-
# Get message while not blocking
122+
# Monitor the socket
123123
try:
124-
# Get the data
125-
bs = self.socket.recv(8)
126-
127-
# If null, skip
128-
if bs == b"":
129-
continue
130-
131-
# Get the length
132-
(length,) = struct.unpack(">Q", bs)
133-
data = b""
134-
135-
# Use the length to get the entire message
136-
while len(data) < int(length):
137-
to_read = int(length) - len(data)
138-
data += self.socket.recv(4096 if to_read > 4096 else to_read)
139-
140-
except socket.timeout:
141-
continue
142-
143-
# If end, stop it
144-
if data == b"":
124+
success, msg = sh.monitor(self.name, self.socket)
125+
except (ConnectionResetError, ConnectionAbortedError):
145126
break
146127

147-
# Decode the message so we can process it
148-
msg = decode_payload(data)
149-
150128
# Process the msg
151-
self.process_msg(msg)
129+
if success:
130+
self.process_msg(msg)
152131

153132
self.socket.close()
154133

155134
def send(self, msg: Dict, ack: bool = False):
156135

157-
# Create an uuid to track the message
158-
msg_uuid = str(uuid.uuid4())
159-
160-
# Convert msg data to bytes
161-
msg_bytes, msg_length = create_payload(
162-
type=self.sender_msg_type,
163-
signal=msg["signal"],
164-
data=msg["data"],
136+
# Sending the data
137+
success, msg_uuid = sh.send(
138+
name=self.name,
139+
s=self.socket,
140+
msg=msg,
141+
sender_msg_type=self.sender_msg_type,
165142
ack=ack,
166-
provided_uuid=msg_uuid,
167143
)
168144

169-
# Send the message
170-
try:
171-
self.socket.sendall(msg_length + msg_bytes)
172-
logger.debug(f"{self}: send {msg['signal']}")
173-
except socket.timeout:
174-
logger.debug(f"{self}: Socket Timeout: skipping")
175-
return
176-
except:
177-
logger.debug(
178-
f"{self}: Broken Pipe Error, handled for {msg['signal']}", exc_info=True
179-
)
180-
return
145+
# If not successful, skip ACK
146+
if not success:
147+
return None
181148

182149
# If requested ACK, wait
183150
if ack:
@@ -198,61 +165,40 @@ def send(self, msg: Dict, ack: bool = False):
198165

199166
miss_counter += 1
200167

201-
def send_folder(self, name: str, dir: pathlib.Path, buffersize: int = 4096):
202-
203-
assert (
204-
dir.is_dir() and dir.exists()
205-
), f"Sending {dir} needs to be a folder that exists."
206-
207-
# First, we need to archive the folder into a zip file
208-
format = "zip"
209-
shutil.make_archive(str(dir), format, dir.parent, dir.name)
210-
zip_file = dir.parent / f"{dir.name}.{format}"
211-
212-
# Relocate zip to the tempfolder
213-
temp_zip_file = self.tempfolder / f"_{zip_file.name}"
214-
shutil.move(zip_file, temp_zip_file)
215-
216-
# Get information about the filesize
217-
filesize = os.path.getsize(temp_zip_file)
218-
max_num_steps = math.ceil(filesize / buffersize)
219-
220-
# Now start the process of sending content to the server
221-
# First, we send the message inciting file transfer
222-
init_msg = {
223-
"type": enums.WORKER_MESSAGE,
224-
"signal": enums.FILE_TRANSFER_START,
225-
"data": {
226-
"name": name,
227-
"filename": f"{dir.name}.{format}",
228-
"filesize": filesize,
229-
"buffersize": buffersize,
230-
"max_num_steps": max_num_steps,
231-
},
232-
}
233-
self.send(init_msg)
234-
logger.debug(f"{self}: sent file transfer initialization")
235-
236-
# Having counter tracking number of messages
237-
msg_counter = 1
238-
239-
with open(temp_zip_file, "rb") as f:
240-
while True:
241-
242-
# Read the data to be sent
243-
data = f.read(buffersize)
244-
if not data:
245-
break
168+
def receive_file(self, msg: Dict[str, Any]):
169+
170+
# Obtain the file
171+
success, sender_name, dst_filepath = sh.file_transfer_receive(
172+
name=self.name, s=self.socket, msg=msg, tempfolder=self.tempfolder
173+
)
174+
175+
# Create a record of the files transferred and from whom
176+
if success:
177+
self.file_transfer_records[sender_name][dst_filepath.name] = dst_filepath
246178

247-
logger.debug(
248-
f"{self}: file transfer, step {msg_counter}/{max_num_steps}"
249-
)
179+
def send_file(self, name: str, filepath: pathlib.Path):
250180

251-
# Send the data
252-
self.socket.sendall(data)
253-
msg_counter += 1
181+
assert filepath.exists() and filepath.is_file()
254182

255-
logger.debug(f"{self}: finished file transfer")
183+
sh.send_file(
184+
sender_name=name,
185+
sender_msg_type=self.sender_msg_type,
186+
s=self.socket,
187+
filepath=filepath,
188+
buffersize=4096,
189+
)
190+
191+
def send_folder(self, name: str, folderpath: pathlib.Path):
192+
193+
assert folderpath.exists() and folderpath.is_dir()
194+
195+
sh.send_folder(
196+
name=name,
197+
s=self.socket,
198+
dir=folderpath,
199+
tempfolder=self.tempfolder,
200+
sender_msg_type=self.sender_msg_type,
201+
)
256202

257203
def shutdown(self):
258204

chimerapy/data_handlers.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
import threading
55
import queue
66

7-
logger = logging.getLogger("chimerapy")
7+
from . import _logger
8+
9+
logger = _logger.getLogger("chimerapy")
810

911
# Internal Imports
1012
from . import enums

0 commit comments

Comments
 (0)