Repository: dbader/schedule

Issue:
Job __repr__ throws AttributeError when job_func is None
```
>>> s._job
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/path/to/my/virtualenv/site-packages/schedule/__init__.py", line 284, in __repr__
    args = [repr(x) if is_repr(x) else str(x) for x in self.job_func.args]
AttributeError: 'NoneType' object has no attribute 'args'
```

Line 284 (and similarly line 285 for kwargs) assumes self.job_func is not None (and has the "args" attribute). I think it needs to use logic similar to line 264 (and line 265, respectively for kwargs). 

It may be helpful to extract line 264 to a reusable property:

```
@property
def _job_func_args(self):
    return () if self.job_func is None else self.job_func.args
```

I can draft a PR if that would be helpful.


For context, I am using `schedule` as a schedule-builder interface, (i.e. just Job definitions without `do`).

Discussion:
coveralls: 
[![Coverage Status](https://coveralls.io/builds/58700524/badge)](https://coveralls.io/builds/58700524)

Coverage: 99.713% (+0.002%) from 99.711% when pulling **6ca4e3252969e1e5cb2d8066109b132a51be7a55 on CPickens42:master** into **a3ecd3548aff70bef15a61baee0d2d7f22f57992 on dbader:master**.


Patch:
diff --git a/schedule/__init__.py b/schedule/__init__.py
index a73c6a6..2db9b0a 100644
--- a/schedule/__init__.py
+++ b/schedule/__init__.py
@@ -292,9 +292,13 @@ class Job:
             job_func_name = self.job_func.__name__
         else:
             job_func_name = repr(self.job_func)
-        args = [repr(x) if is_repr(x) else str(x) for x in self.job_func.args]
-        kwargs = ["%s=%s" % (k, repr(v)) for k, v in self.job_func.keywords.items()]
-        call_repr = job_func_name + "(" + ", ".join(args + kwargs) + ")"
+
+        if self.job_func is not None:
+            args = [repr(x) if is_repr(x) else str(x) for x in self.job_func.args]
+            kwargs = ["%s=%s" % (k, repr(v)) for k, v in self.job_func.keywords.items()]
+            call_repr = job_func_name + "(" + ", ".join(args + kwargs) + ")"
+        else:
+            call_repr = "[None]"
 
         if self.at_time is not None:
             return "Every %s %s at %s do %s %s" % (


Test patch:
diff --git a/test_schedule.py b/test_schedule.py
index 31f8167..7d4617e 100644
--- a/test_schedule.py
+++ b/test_schedule.py
@@ -661,6 +661,10 @@ class SchedulerTests(unittest.TestCase):
             )
         )
 
+        # Ensure Job.__repr__ does not throw exception on a partially-composed Job
+        s3 = repr(schedule.every(10))
+        assert s3 == "Every 10 None do [None] (last run: [never], next run: [never])"
+
     def test_to_string_lambda_job_func(self):
         assert len(str(every().minute.do(lambda: 1))) > 1
         assert len(str(every().day.at("10:30").do(lambda: 1))) > 1