Skip to content

Commit

Permalink
Fix #1171, django_tee bug
Browse files Browse the repository at this point in the history
Fix django_tee bug when calling this command,
* remove return_value, as Django management commands don't support it
* remove exit_code, as Django management commands don't support it
* add a NullBooleanField finished_successfully
  • Loading branch information
mpasternak committed Jan 9, 2022
1 parent 6677d30 commit e140111
Show file tree
Hide file tree
Showing 10 changed files with 63 additions and 57 deletions.
3 changes: 1 addition & 2 deletions src/tee/admin.py
Expand Up @@ -19,8 +19,7 @@ class LogAdmin(admin.ModelAdmin):
readonly_fields = [
"started_on",
"finished_on",
"exit_code",
"exit_value",
"finished_successfully",
"command_name",
"args",
"stdout",
Expand Down
1 change: 1 addition & 0 deletions src/tee/const.py
@@ -0,0 +1 @@
DONT_CALL = "Used by django_tee tests. Don't call, does nothing. "
25 changes: 4 additions & 21 deletions src/tee/core.py
Expand Up @@ -2,7 +2,6 @@
import traceback
from contextlib import redirect_stderr, redirect_stdout

import simplejson
from django.core.management import ManagementUtility

from tee.models import Log
Expand All @@ -21,37 +20,21 @@ def execute(argv, **kwargs):
stdout = TeeIO(kwargs.get("stdout") if "stdout" in kwargs else sys.stdout)
stderr = TeeIO(kwargs.get("stderr") if "stderr" in kwargs else sys.stdout)

log = Log.objects.create(command_name=argv[1], args=argv[2:])
res = 127
log = Log.objects.create(command_name=argv[0], args=argv[1:])

try:
with redirect_stderr(stderr):
with redirect_stdout(stdout):
try:
utility = ManagementUtility(argv)
utility.execute()
log.finished_successfully = True
except Exception:
res = 1
log.finished_successfully = False
log.traceback = traceback.format_exc(limit=65535)

finally:
log.stdout = stdout.getvalue()
log.stderr = stderr.getvalue()
log.finished_on = timezone.now()

if res is None:
log.exit_code = 0
elif isinstance(res, int):
log.exit_code = res
else:
log.exit_code = -1

# if the return value is json-encodeable, save it to db:
try:
simplejson.dumps(res)
log.exit_value = res
except TypeError:
log.exit_value = (
"Unable to encode results as JSON, thus unable to store it "
"in the database. Repr: %r" % res
)
log.save()
11 changes: 9 additions & 2 deletions src/tee/management/commands/tee.py
Expand Up @@ -11,5 +11,12 @@ def add_arguments(self, parser):
parser.add_argument("otherthings", nargs="*") # parser.add

def handle(self, command_name, otherthings, *args, **options):
new_argv = [sys.argv[0]] + sys.argv[2:]
core.execute(new_argv)
new_argv = [sys.argv[0], command_name] + otherthings

kwargs = {}

for elem in ["stdout", "stderr"]:
if options.get(elem):
kwargs[elem] = options.get(elem)

core.execute(new_argv, **kwargs)
4 changes: 4 additions & 0 deletions src/tee/management/commands/tee_test_exception.py
@@ -1,7 +1,11 @@
from django.core.management import BaseCommand

from tee import const


class Command(BaseCommand):
help = const.DONT_CALL

def handle(self, *args, **options):

self.stderr.write("wrote to stderr")
Expand Down
4 changes: 4 additions & 0 deletions src/tee/management/commands/tee_test_okay.py
@@ -1,7 +1,11 @@
from django.core.management import BaseCommand

from tee import const


class Command(BaseCommand):
help = const.DONT_CALL

def handle(self, *args, **options):

self.stderr.write("wrote to stderr")
Expand Down
6 changes: 0 additions & 6 deletions src/tee/management/commands/tee_test_result.py

This file was deleted.

30 changes: 30 additions & 0 deletions src/tee/migrations/0004_auto_20220109_0151.py
@@ -0,0 +1,30 @@
# Generated by Django 3.0.14 on 2022-01-09 00:51

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("tee", "0003_log_traceback"),
]

operations = [
migrations.RemoveField(
model_name="log",
name="exit_code",
),
migrations.RemoveField(
model_name="log",
name="exit_value",
),
migrations.RemoveField(
model_name="log",
name="kwargs",
),
migrations.AddField(
model_name="log",
name="finished_successfully",
field=models.NullBooleanField(),
),
]
16 changes: 1 addition & 15 deletions src/tee/models.py
Expand Up @@ -6,21 +6,7 @@
class Log(models.Model):
started_on = models.DateTimeField(auto_now_add=True)
finished_on = models.DateTimeField(blank=True, null=True)

exit_code = models.SmallIntegerField(
blank=True,
null=True,
help_text="""If value returned by
django.core.management.call_command is None, this will be zero. If value returned by d.c.m.call_command
is an int, this will be that int. If different, this is set to -1 and exit_value field is set. """,
)

exit_value = JSONField(
blank=True,
null=True,
help_text="""JSON-encoded value, returned by
django.core.management.call_command function, if different than None or an int. """,
)
finished_successfully = models.NullBooleanField()

command_name = models.TextField()
args = JSONField(blank=True, null=True)
Expand Down
20 changes: 9 additions & 11 deletions src/tee/tests.py
Expand Up @@ -17,20 +17,18 @@ def stderr():


@pytest.mark.django_db
def test_tee_okay(stdout: StringIO, stderr: StringIO):
call_command("tee", "tee_test_okay", stdout=stdout, stderr=stderr)
assert Log.objects.first().exit_code == 0
def test_tee_okay(stdout: StringIO, stderr: StringIO, mocker):
with mocker.patch("django.db.connections.close_all"):
# patch wymagany, bo BaseCommand wywołuje close_all
call_command("tee", "tee_test_okay", stdout=stdout, stderr=stderr)
assert Log.objects.first().finished_successfully
assert "Used print()" in stdout.getvalue()
assert "Used print()" in stderr.getvalue()


@pytest.mark.django_db
def test_tee_exception(stdout, stderr):
call_command("tee", "tee_test_exception", stdout=stdout, stderr=stderr)
def test_tee_exception(stdout, stderr, mocker):
with mocker.patch("django.db.connections.close_all"):
# patch wymagany, bo BaseCommand wywołuje close_all
call_command("tee", "tee_test_exception", stdout=stdout, stderr=stderr)
assert Log.objects.first().traceback


@pytest.mark.django_db
def test_tee_result(stdout, stderr):
call_command("tee", "tee_test_result", stdout=stdout, stderr=stderr)
assert "Unable to encode" in Log.objects.first().exit_value

0 comments on commit e140111

Please sign in to comment.