Skip to content

Commit 3e69d15

Browse files
author
sajidurrahman
committed
chore: adjusted README.md
chore: project name modified chore: fixed tests chore: on-push CI chore: simplified CI with lightweight clamav image chore: fixed test_version chore: disclaimer and installation steps in README.md chore: better usage example in README.md and test renamed chore: better usage example
1 parent 7fbc7e4 commit 3e69d15

File tree

4 files changed

+119
-66
lines changed

4 files changed

+119
-66
lines changed

.github/workflows/ci.yml

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main, master]
6+
pull_request:
7+
branches: [main, master]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
fail-fast: false
14+
matrix:
15+
python-version: ["3.10", "3.11", "3.12"]
16+
17+
services:
18+
clamav:
19+
image: clamav/clamav:stable
20+
ports:
21+
- 3310:3310
22+
env:
23+
CLAMAV_NO_FRESHCLAMD: "true"
24+
CLAMAV_NO_MILTERD: "true"
25+
options: >-
26+
--health-cmd="echo PING | nc localhost 3310 | grep -q PONG"
27+
--health-interval=30s
28+
--health-timeout=30s
29+
--health-retries=30
30+
--health-start-period=300s
31+
32+
steps:
33+
- uses: actions/checkout@v4
34+
35+
- name: Set up Python ${{ matrix.python-version }}
36+
uses: actions/setup-python@v5
37+
with:
38+
python-version: ${{ matrix.python-version }}
39+
40+
- name: Install dependencies
41+
run: |
42+
python -m pip install --upgrade pip
43+
pip install pytest
44+
pip install -e .
45+
46+
- name: Wait for ClamAV to be ready
47+
run: |
48+
echo "Waiting for ClamAV service..."
49+
timeout=600
50+
elapsed=0
51+
while [ $elapsed -lt $timeout ]; do
52+
if echo PING | nc -w 5 localhost 3310 2>/dev/null | grep -q PONG; then
53+
echo "ClamAV is ready after ${elapsed}s!"
54+
exit 0
55+
fi
56+
echo "Waiting... (${elapsed}s elapsed)"
57+
sleep 15
58+
elapsed=$((elapsed + 15))
59+
done
60+
echo "ClamAV failed to start within ${timeout}s"
61+
docker ps -a
62+
docker logs $(docker ps -aq --filter "ancestor=clamav/clamav:stable") || true
63+
exit 1
64+
65+
- name: Run tests
66+
run: |
67+
pytest src/tests/ -v
68+
69+
lint:
70+
runs-on: ubuntu-latest
71+
steps:
72+
- uses: actions/checkout@v4
73+
74+
- name: Set up Python
75+
uses: actions/setup-python@v5
76+
with:
77+
python-version: "3.12"
78+
79+
- name: Install linting tools
80+
run: |
81+
python -m pip install --upgrade pip
82+
pip install ruff
83+
84+
- name: Run linter
85+
run: |
86+
ruff check src/
87+

README.md

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,30 +20,37 @@ This is a fork of the original [python-clamd](https://github.com/graingert/pytho
2020
## Installation
2121

2222
```bash
23-
pip install python-clamd-fork
23+
pip install git+https://github.com/userlike/python-clamd.git
2424
```
2525

2626
## Usage
2727

2828
```python
2929
import clamd
30+
from io import BytesIO
3031

31-
# Connect via Unix socket
32-
cd = clamd.ClamdUnixSocket()
33-
34-
# Or connect via network
32+
# Connect via network
3533
cd = clamd.ClamdNetworkSocket(host='127.0.0.1', port=3310)
3634

3735
# Ping the daemon
38-
cd.ping()
36+
cd.ping() # Returns "PONG", otherwise raises ConnectionError or ResponseError
37+
38+
# Scan content using instream (recommended - works with remote ClamAV)
39+
result = cd.instream(BytesIO(b'file content here'))
40+
# Returns: {'stream': ('OK', None)} for clean files
41+
# Returns: {'stream': ('FOUND', 'VirusName')} for infected files
3942

40-
# Scan a file
43+
# Scan a file by path (only works if ClamAV can access the path)
4144
cd.scan('/path/to/file')
4245

4346
# Get version info
4447
cd.version()
4548
```
4649

50+
## Disclaimer
51+
52+
**USE AT YOUR OWN RISK.** This software is provided "as is", without warranty of any kind, express or implied. The authors and maintainers do not guarantee the correctness, reliability, or functionality of this package. We shall not be held liable for any damages, losses, or consequences arising from the use of this software. Please review the source code before using it in any production or critical environment.
53+
4754
## License
4855

4956
LGPL - See the original [python-clamd](https://github.com/graingert/python-clamd) for details.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[project]
2-
name = "python-clamd-fork"
2+
name = "clamd"
33
version = "0.1.0"
44
description = "It is a fork of https://github.com/graingert/python-clamd. It was forked to cleanup some deprecated code."
55
readme = "README.md"

src/tests/test_api.py

Lines changed: 17 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,46 @@
1-
#!/usr/bin/env python
2-
# -*- coding: utf-8 -*-
3-
from __future__ import unicode_literals
41
import clamd
52
from io import BytesIO
6-
from contextlib import contextmanager
7-
import tempfile
8-
import shutil
9-
import os
10-
import stat
113

124
import pytest
135

14-
mine = (stat.S_IREAD | stat.S_IWRITE)
15-
other = stat.S_IROTH
16-
execute = (stat.S_IEXEC | stat.S_IXOTH)
176

18-
19-
@contextmanager
20-
def mkdtemp(*args, **kwargs):
21-
temp_dir = tempfile.mkdtemp(*args, **kwargs)
22-
try:
23-
yield temp_dir
24-
finally:
25-
shutil.rmtree(temp_dir)
26-
27-
28-
class TestUnixSocket(object):
7+
class TestNetworkSocket(object):
8+
"""
9+
Test suite expects ClamAV running at default host and port (localhost, 3310)
10+
"""
2911
kwargs = {}
3012

13+
@pytest.fixture(autouse=True)
3114
def setup(self):
32-
self.cd = clamd.ClamdUnixSocket(**self.kwargs)
15+
self.cd = clamd.ClamdNetworkSocket(**self.kwargs)
3316

3417
def test_ping(self):
35-
assert self.cd.ping()
18+
result = self.cd.ping()
19+
assert result == "PONG"
3620

3721
def test_version(self):
38-
assert self.cd.version().startswith("ClamAV")
22+
version = self.cd.version()
23+
assert "ClamAV 1.5.1" in version
3924

4025
def test_reload(self):
4126
assert self.cd.reload() == 'RELOADING'
4227

43-
def test_scan(self):
44-
with tempfile.NamedTemporaryFile('wb', prefix="python-clamd") as f:
45-
f.write(clamd.EICAR)
46-
f.flush()
47-
os.fchmod(f.fileno(), (mine | other))
48-
expected = {f.name: ('FOUND', 'Eicar-Test-Signature')}
49-
50-
assert self.cd.scan(f.name) == expected
51-
52-
def test_unicode_scan(self):
53-
with tempfile.NamedTemporaryFile('wb', prefix=u"python-clamdλ") as f:
54-
f.write(clamd.EICAR)
55-
f.flush()
56-
os.fchmod(f.fileno(), (mine | other))
57-
expected = {f.name: ('FOUND', 'Eicar-Test-Signature')}
5828

59-
assert self.cd.scan(f.name) == expected
60-
61-
def test_multiscan(self):
62-
expected = {}
63-
with mkdtemp(prefix="python-clamd") as d:
64-
for i in range(10):
65-
with open(os.path.join(d, "file" + str(i)), 'wb') as f:
66-
f.write(clamd.EICAR)
67-
os.fchmod(f.fileno(), (mine | other))
68-
expected[f.name] = ('FOUND', 'Eicar-Test-Signature')
69-
os.chmod(d, (mine | other | execute))
70-
71-
assert self.cd.multiscan(d) == expected
72-
73-
def test_instream(self):
29+
def test_instream_found(self):
30+
# Case: True positive
7431
expected = {'stream': ('FOUND', 'Eicar-Test-Signature')}
7532
assert self.cd.instream(BytesIO(clamd.EICAR)) == expected
7633

77-
def test_insteam_success(self):
34+
35+
def test_instream_ok(self):
36+
# Case True negative
7837
assert self.cd.instream(BytesIO(b"foo")) == {'stream': ('OK', None)}
7938

8039

81-
class TestUnixSocketTimeout(TestUnixSocket):
40+
class TestUnixSocketTimeout(TestNetworkSocket):
8241
kwargs = {"timeout": 20}
8342

8443

8544
def test_cannot_connect():
8645
with pytest.raises(clamd.ConnectionError):
87-
clamd.ClamdUnixSocket(path="/tmp/404").ping()
46+
clamd.ClamdNetworkSocket(host="another_host").ping()

0 commit comments

Comments
 (0)