Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ dependencies = [
"lxml>=5.2.0",
"pillow>=10.3.0",
"numpy>=1.26.0",
"grpcio>=1.68.0",
"grpcio-tools>=1.68.0",
]

[tool.uv]
Expand Down
108 changes: 108 additions & 0 deletions python-examples/GRPC_README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# gRPC Python Example

This example demonstrates a simple gRPC client-server application based on the [official Python quickstart guide](https://grpc.io/docs/languages/python/quickstart/).

## Overview

The example implements a `Greeter` service with two RPC methods:
- `SayHello`: Returns a greeting message
- `SayHelloAgain`: Returns another greeting message

## Files

- `grpc_example.proto` - Protocol Buffer definition file
- `grpcio-example.py` - Complete client/server implementation

## Requirements

```bash
pip install grpcio grpcio-tools
```

## Setup

First, generate the gRPC Python code from the proto file:

```bash
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. python-examples/grpc_example.proto
```

Or use the built-in setup command:

```bash
cd python-examples
python grpcio-example.py setup
```

This generates:
- `grpc_example_pb2.py` - Protocol buffer message classes
- `grpc_example_pb2_grpc.py` - gRPC service classes

## Running the Example

### Terminal 1 - Start the server

```bash
cd python-examples
python grpcio-example.py server
```

Output:
```
✓ gRPC Server started on port 50051
Waiting for client connections...
Press Ctrl+C to stop
```

### Terminal 2 - Run the client

```bash
cd python-examples
python grpcio-example.py client
```

Output:
```
Connecting to gRPC server at localhost:50051...

1. Calling SayHello RPC...
✓ Client received: Hello, World!

2. Calling SayHelloAgain RPC...
✓ Client received: Hello again, gRPC User!

✓ All RPC calls completed successfully!
```

## Custom Port

Start server on a different port:
```bash
python grpcio-example.py server 50052
```

Connect client to custom port:
```bash
python grpcio-example.py client localhost 50052
```

## Usage

```
python grpcio-example.py setup - Generate proto files
python grpcio-example.py server [port] - Start server (default: 50051)
python grpcio-example.py client [host] [port] - Run client (default: localhost:50051)
```

## Notes

- This example uses **insecure channels** for simplicity
- In production, use secure channels with TLS/SSL certificates
- The server runs indefinitely until Ctrl+C is pressed
- Each client invocation makes two RPC calls and exits

## Learn More

- [gRPC Python Documentation](https://grpc.io/docs/languages/python/)
- [Protocol Buffers](https://protobuf.dev/)
- [gRPC Core Concepts](https://grpc.io/docs/what-is-grpc/core-concepts/)
63 changes: 50 additions & 13 deletions python-examples/djvu-pdf-example.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import fnmatch
import os
import subprocess
import shutil
from pathlib import Path
# global variables (change to suit your needs)
inputfolderpath = '~' # set to import folder path
outputpath = '~' # set to output folder (must exist)
Expand All @@ -26,32 +28,67 @@ def find_files(directory, pattern):
for filename in find_files(inputfolderpath, '*.djvu'):
print(f"[*] Processing DJVU to PDF for {filename}...")
i = i + 1
inputfull = inputfolderpath+filename
outputfilename = filename[:-4]+i+'pdf' # make filename unique
outputfilepath = outputpath
p = subprocess.Popen(["djvu2pdf", inputfull], stdout=subprocess.PIPE)
inputfull = os.path.join(inputfolderpath, filename)
# Validate that the file exists and is a regular file
if not os.path.isfile(inputfull):
print(f"[!] Skipping {filename} - not a valid file")
continue
outputfilename = f"{filename[:-5]}_{i}.pdf" # make filename unique
outputfilepath = os.path.join(outputpath, outputfilename)
# Use list for subprocess to avoid shell injection
p = subprocess.Popen(
["djvu2pdf", inputfull],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
output, err = p.communicate()
subprocess.call(["mv", outputfilename, outputfilepath])
# Use shutil.move instead of shell command for better security
if p.returncode == 0 and os.path.exists(outputfilename):
shutil.move(outputfilename, outputfilepath)
print('[-] Processing finished for %s' % filename)
print(f"[--] processed {i} file(s) [--]")
exit('\n\"Sanity is madness put to good uses.\" - George Santayana\n')

elif operationtype == '2':
filename = input('What filename to process? (leave blank for example): ')
if 'djvu' in filename:
if filename and 'djvu' in filename:
# Validate filename to prevent path traversal
safe_path = Path(filename).resolve()
if not safe_path.is_file() or not str(safe_path).endswith('.djvu'):
print('[!] Invalid file or not a .djvu file')
exit('Invalid input')
print('Processing DJVU to PDF...')
p = subprocess.Popen(["djvu2pdf", filename], stdout=subprocess.PIPE)
p = subprocess.Popen(
["djvu2pdf", str(safe_path)],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
output, err = p.communicate()
print('Processing finished')
exit('Completed sucessfully')
if p.returncode == 0:
print('Processing finished')
exit('Completed successfully')
else:
print(f'[!] Error processing file: {err.decode() if err else "Unknown error"}')
exit('Failed')
else:
print('No djvu file to process, running sample')
print('Processing DJVU to PDF...')
p = subprocess.Popen(["djvu2pdf", "assets/example.djvu"],
stdout=subprocess.PIPE)
sample_file = Path("assets/example.djvu")
if not sample_file.is_file():
print('[!] Sample file not found')
exit('Sample file missing')
p = subprocess.Popen(
["djvu2pdf", str(sample_file)],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
output, err = p.communicate()
print('Processing finished')
exit('Completed sucessfully')
if p.returncode == 0:
print('Processing finished')
exit('Completed successfully')
else:
print(f'[!] Error: {err.decode() if err else "Unknown error"}')
exit('Failed')


elif operationtype == '':
Expand Down
63 changes: 47 additions & 16 deletions python-examples/flask-example.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,26 +39,57 @@ def hello_world():
@app.route("/upload", methods=["POST"])
def upload_csv() -> str:
"""Upload CSV example."""
if "file" not in request.files:
return jsonify({"status": HTTPStatus.BAD_REQUEST, "message": "No file provided"}), 400

submitted_file = request.files["file"]
if submitted_file and allowed_filename(submitted_file.filename):
filename = secure_filename(submitted_file.filename)
directory = os.path.join(app.config["UPLOAD_FOLDER"])
if not os.path.exists(directory):
os.mkdir(directory)
basedir = os.path.abspath(os.path.dirname(__file__))
submitted_file.save(
os.path.join(basedir, app.config["UPLOAD_FOLDER"], filename)
)
out = {
"status": HTTPStatus.OK,
"filename": filename,
"message": f"{filename} saved successful.",
}
return jsonify(out)

if not submitted_file or not submitted_file.filename:
return jsonify({"status": HTTPStatus.BAD_REQUEST, "message": "No file selected"}), 400

if not allowed_filename(submitted_file.filename):
return jsonify({"status": HTTPStatus.BAD_REQUEST, "message": "File type not allowed"}), 400

filename = secure_filename(submitted_file.filename)

# Additional security check: ensure filename is not empty after sanitization
if not filename:
return jsonify({"status": HTTPStatus.BAD_REQUEST, "message": "Invalid filename"}), 400

basedir = os.path.abspath(os.path.dirname(__file__))
upload_folder = os.path.abspath(os.path.join(basedir, app.config["UPLOAD_FOLDER"]))

# Create directory with secure permissions if it doesn't exist
if not os.path.exists(upload_folder):
os.makedirs(upload_folder, mode=0o755, exist_ok=True)

# Construct full path and verify it's within the upload directory (prevent path traversal)
file_path = os.path.abspath(os.path.join(upload_folder, filename))

if not file_path.startswith(upload_folder):
return jsonify({"status": HTTPStatus.BAD_REQUEST, "message": "Invalid file path"}), 400

# Limit file size (optional but recommended)
# submitted_file.seek(0, os.SEEK_END)
# file_size = submitted_file.tell()
# submitted_file.seek(0)
# if file_size > MAX_FILE_SIZE:
# return jsonify({"status": HTTPStatus.BAD_REQUEST, "message": "File too large"}), 400

submitted_file.save(file_path)

out = {
"status": HTTPStatus.OK,
"filename": filename,
"message": f"{filename} saved successfully.",
}
return jsonify(out)


if __name__ == "__main__":
app.config["UPLOAD_FOLDER"] = "flaskme/"
app.run(port=6969, debug=True)
# Debug mode disabled for security - use environment variable to enable in development
debug_mode = os.environ.get("FLASK_DEBUG", "False").lower() == "true"
app.run(port=6969, debug=debug_mode)

# curl -X POST localhost:6969/upload -F file=@"assets/archive_name.tar.gz" -i
21 changes: 21 additions & 0 deletions python-examples/grpc_example.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
syntax = "proto3";

package grpc_example;

// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
// Sends another greeting
rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
string name = 1;
}

// The response message containing the greeting.
message HelloReply {
string message = 1;
}
Loading
Loading