From b99eecc1ae4f3ea627b0f71144e172c9eb745023 Mon Sep 17 00:00:00 2001
From: Robert Sparks <rjsparks@nostrum.com>
Date: Tue, 3 Aug 2021 22:22:35 +0000
Subject: [PATCH] Bring the factory-boy and Faker dependencies forward through
 some minor breaking changes. Stop using the deprecated fr_QC locale in Faker.
 Commit ready for merge.  - Legacy-Id: 19270

---
 ietf/dbtemplate/factories.py   |  2 +-
 ietf/doc/factories.py          | 12 ++++++------
 ietf/group/factories.py        | 12 ++++++------
 ietf/iesg/factories.py         |  2 +-
 ietf/ietfauth/factories.py     |  2 +-
 ietf/ipr/factories.py          | 10 +++++-----
 ietf/liaisons/factories.py     |  6 +++---
 ietf/mailinglists/factories.py |  2 +-
 ietf/meeting/factories.py      | 16 ++++++++--------
 ietf/nomcom/factories.py       | 18 ++++++++++--------
 ietf/person/factories.py       | 14 +++++++-------
 ietf/review/factories.py       |  8 ++++----
 ietf/stats/factories.py        |  2 +-
 ietf/submit/factories.py       |  4 ++--
 requirements.txt               |  6 ++++--
 15 files changed, 60 insertions(+), 56 deletions(-)

diff --git a/ietf/dbtemplate/factories.py b/ietf/dbtemplate/factories.py
index 36a956dba..92db4447b 100644
--- a/ietf/dbtemplate/factories.py
+++ b/ietf/dbtemplate/factories.py
@@ -2,7 +2,7 @@ import factory
 
 from ietf.dbtemplate.models import DBTemplate
 
-class DBTemplateFactory(factory.DjangoModelFactory):
+class DBTemplateFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = DBTemplate
 
diff --git a/ietf/doc/factories.py b/ietf/doc/factories.py
index 699f972ea..946f2ebe7 100644
--- a/ietf/doc/factories.py
+++ b/ietf/doc/factories.py
@@ -29,7 +29,7 @@ def draft_name_generator(type_id,group,n):
               n,
             )
 
-class BaseDocumentFactory(factory.DjangoModelFactory):
+class BaseDocumentFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = Document
 
@@ -259,7 +259,7 @@ class ReviewFactory(BaseDocumentFactory):
     name = factory.LazyAttribute(lambda o: 'review-doesnotexist-00-%s-%s'%(o.group.acronym,datetime.date.today().isoformat()))
     group = factory.SubFactory('ietf.group.factories.GroupFactory',type_id='review')
 
-class DocAliasFactory(factory.DjangoModelFactory):
+class DocAliasFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = DocAlias
 
@@ -276,7 +276,7 @@ class DocAliasFactory(factory.DjangoModelFactory):
                     self.docs.add(doc)
 
 
-class DocEventFactory(factory.DjangoModelFactory):
+class DocEventFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = DocEvent
 
@@ -326,7 +326,7 @@ class StateDocEventFactory(DocEventFactory):
         obj.save()
 
 # All of these Ballot* factories are extremely skeletal. Flesh them out as needed by tests.
-class BallotTypeFactory(factory.DjangoModelFactory):
+class BallotTypeFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = BallotType
         django_get_or_create = ('slug','doc_type_id')
@@ -363,14 +363,14 @@ class BallotPositionDocEventFactory(DocEventFactory):
     balloter = factory.SubFactory('ietf.person.factories.PersonFactory')
     pos_id = 'discuss'
 
-class DocumentActionHolderFactory(factory.DjangoModelFactory):
+class DocumentActionHolderFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = DocumentActionHolder
         
     document = factory.SubFactory(WgDraftFactory)
     person = factory.SubFactory('ietf.person.factories.PersonFactory')
 
-class DocumentAuthorFactory(factory.DjangoModelFactory):
+class DocumentAuthorFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = DocumentAuthor
 
diff --git a/ietf/group/factories.py b/ietf/group/factories.py
index 163ebb52e..d8d927d80 100644
--- a/ietf/group/factories.py
+++ b/ietf/group/factories.py
@@ -9,7 +9,7 @@ from ietf.group.models import Group, Role, GroupEvent, GroupMilestone, \
                               GroupHistory, RoleHistory
 from ietf.review.factories import ReviewTeamSettingsFactory
 
-class GroupFactory(factory.DjangoModelFactory):
+class GroupFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = Group
         django_get_or_create = ('acronym',)
@@ -39,7 +39,7 @@ class ReviewTeamFactory(GroupFactory):
     def settings(obj, create, extracted, **kwargs):
         ReviewTeamSettingsFactory.create(group=obj,**kwargs)
 
-class RoleFactory(factory.DjangoModelFactory):
+class RoleFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = Role
 
@@ -47,7 +47,7 @@ class RoleFactory(factory.DjangoModelFactory):
     person = factory.SubFactory('ietf.person.factories.PersonFactory')
     email = factory.LazyAttribute(lambda obj: obj.person.email())
 
-class GroupEventFactory(factory.DjangoModelFactory):
+class GroupEventFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = GroupEvent
 
@@ -56,7 +56,7 @@ class GroupEventFactory(factory.DjangoModelFactory):
     type = 'comment'
     desc = factory.Faker('paragraph')
 
-class BaseGroupMilestoneFactory(factory.DjangoModelFactory):
+class BaseGroupMilestoneFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = GroupMilestone
 
@@ -72,7 +72,7 @@ class DatelessGroupMilestoneFactory(BaseGroupMilestoneFactory):
     group = factory.SubFactory(GroupFactory, uses_milestone_dates=False)
     order = factory.Sequence(lambda n: n)
 
-class GroupHistoryFactory(factory.DjangoModelFactory):
+class GroupHistoryFactory(factory.django.DjangoModelFactory):
     class Meta:
         model=GroupHistory
 
@@ -86,7 +86,7 @@ class GroupHistoryFactory(factory.DjangoModelFactory):
     group = factory.SubFactory(GroupFactory)
     acronym = factory.LazyAttribute(lambda obj: obj.group.acronym)
 
-class RoleHistoryFactory(factory.DjangoModelFactory):
+class RoleHistoryFactory(factory.django.DjangoModelFactory):
     class Meta:
         model=RoleHistory
 
diff --git a/ietf/iesg/factories.py b/ietf/iesg/factories.py
index 504502328..a9c8bed56 100644
--- a/ietf/iesg/factories.py
+++ b/ietf/iesg/factories.py
@@ -7,7 +7,7 @@ import factory
 from ietf.iesg.models import TelechatAgendaItem
 
 
-class IESGMgmtItemFactory(factory.DjangoModelFactory):
+class IESGMgmtItemFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = TelechatAgendaItem
 
diff --git a/ietf/ietfauth/factories.py b/ietf/ietfauth/factories.py
index 8362d2798..b68b0cece 100644
--- a/ietf/ietfauth/factories.py
+++ b/ietf/ietfauth/factories.py
@@ -11,7 +11,7 @@ from oidc_provider.models import Client as OidClientRecord, ResponseType
 
 from ietf.person.factories import UserFactory, PersonFactory
 
-class OidClientRecordFactory(factory.DjangoModelFactory):
+class OidClientRecordFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = OidClientRecord
 
diff --git a/ietf/ipr/factories.py b/ietf/ipr/factories.py
index 3d31895b7..da1262912 100644
--- a/ietf/ipr/factories.py
+++ b/ietf/ipr/factories.py
@@ -14,13 +14,13 @@ from ietf.ipr.models import (
 def _fake_patent_info():
     return "Date: %s\nNotes: %s\nTitle: %s\nNumber: %s\nInventor: %s\n" % (
         (datetime.datetime.today()-datetime.timedelta(days=365)).strftime("%Y-%m-%d"),
-        factory.Faker('paragraph').generate({}),
-        factory.Faker('sentence', nb_words=8).generate({}),
+        factory.Faker('paragraph'),
+        factory.Faker('sentence', nb_words=8),
         'US9999999',
-        factory.Faker('name').generate({}),
+        factory.Faker('name'),
     )
 
-class IprDisclosureBaseFactory(factory.DjangoModelFactory):
+class IprDisclosureBaseFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = IprDisclosureBase
 
@@ -83,7 +83,7 @@ class GenericIprDisclosureFactory(IprDisclosureBaseFactory):
     holder_contact_email = factory.Faker('email')
     holder_contact_name = factory.Faker('name')
     
-class IprEventFactory(factory.DjangoModelFactory):
+class IprEventFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = IprEvent
 
diff --git a/ietf/liaisons/factories.py b/ietf/liaisons/factories.py
index 3cdcec469..61788817e 100644
--- a/ietf/liaisons/factories.py
+++ b/ietf/liaisons/factories.py
@@ -3,7 +3,7 @@ import factory
 from ietf.group.factories import GroupFactory
 from ietf.liaisons.models import LiaisonStatement, LiaisonStatementEvent, LiaisonStatementAttachment
 
-class LiaisonStatementFactory(factory.DjangoModelFactory):
+class LiaisonStatementFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = LiaisonStatement
 
@@ -30,7 +30,7 @@ class LiaisonStatementFactory(factory.DjangoModelFactory):
                 obj.to_groups.add(GroupFactory(type_id='wg'))
 
 
-class LiaisonStatementEventFactory(factory.DjangoModelFactory):
+class LiaisonStatementEventFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = LiaisonStatementEvent
 
@@ -40,7 +40,7 @@ class LiaisonStatementEventFactory(factory.DjangoModelFactory):
     desc = factory.Faker('sentence')
 
 
-class LiaisonStatementAttachmentFactory(factory.DjangoModelFactory):
+class LiaisonStatementAttachmentFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = LiaisonStatementAttachment
 
diff --git a/ietf/mailinglists/factories.py b/ietf/mailinglists/factories.py
index 1949cafc6..bc6b2b820 100644
--- a/ietf/mailinglists/factories.py
+++ b/ietf/mailinglists/factories.py
@@ -7,7 +7,7 @@ import random
 
 from ietf.mailinglists.models import List
 
-class ListFactory(factory.DjangoModelFactory):
+class ListFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = List
 
diff --git a/ietf/meeting/factories.py b/ietf/meeting/factories.py
index f872ed64d..ac066e25c 100644
--- a/ietf/meeting/factories.py
+++ b/ietf/meeting/factories.py
@@ -13,7 +13,7 @@ from ietf.name.models import SessionStatusName
 from ietf.group.factories import GroupFactory
 from ietf.person.factories import PersonFactory
 
-class MeetingFactory(factory.DjangoModelFactory):
+class MeetingFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = Meeting
 
@@ -75,7 +75,7 @@ class MeetingFactory(factory.DjangoModelFactory):
             obj.schedule = ScheduleFactory(meeting=obj)
             obj.save()
 
-class SessionFactory(factory.DjangoModelFactory):
+class SessionFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = Session
 
@@ -116,7 +116,7 @@ class SessionFactory(factory.DjangoModelFactory):
             ts = obj.meeting.timeslot_set.all()
             obj.timeslotassignments.create(timeslot=ts[random.randrange(len(ts))],schedule=obj.meeting.schedule)
 
-class ScheduleFactory(factory.DjangoModelFactory):
+class ScheduleFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = Schedule
 
@@ -124,7 +124,7 @@ class ScheduleFactory(factory.DjangoModelFactory):
     name = factory.Sequence(lambda n: 'schedule_%d'%n)
     owner = factory.SubFactory(PersonFactory)
 
-class RoomFactory(factory.DjangoModelFactory):
+class RoomFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = Room
 
@@ -140,7 +140,7 @@ class RoomFactory(factory.DjangoModelFactory):
                 obj.session_types.add(st)
 
 
-class TimeSlotFactory(factory.DjangoModelFactory):
+class TimeSlotFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = TimeSlot
 
@@ -164,7 +164,7 @@ class TimeSlotFactory(factory.DjangoModelFactory):
     def duration(self):
         return datetime.timedelta(minutes=30+random.randrange(9)*15)
 
-class SessionPresentationFactory(factory.DjangoModelFactory):
+class SessionPresentationFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = SessionPresentation
 
@@ -174,7 +174,7 @@ class SessionPresentationFactory(factory.DjangoModelFactory):
     def rev(self):
         return self.document.rev
 
-class FloorPlanFactory(factory.DjangoModelFactory):
+class FloorPlanFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = FloorPlan
 
@@ -190,7 +190,7 @@ class FloorPlanFactory(factory.DjangoModelFactory):
             )
         )
 
-class SlideSubmissionFactory(factory.DjangoModelFactory):
+class SlideSubmissionFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = SlideSubmission
 
diff --git a/ietf/nomcom/factories.py b/ietf/nomcom/factories.py
index 04db3a6ac..8ef4e07fa 100644
--- a/ietf/nomcom/factories.py
+++ b/ietf/nomcom/factories.py
@@ -5,6 +5,8 @@
 import factory
 import random
 
+from faker import Faker
+
 from ietf.nomcom.models import NomCom, Position, Feedback, Nominee, NomineePosition, Nomination, Topic
 from ietf.group.factories import GroupFactory
 from ietf.person.factories import PersonFactory, UserFactory
@@ -79,7 +81,7 @@ def nomcom_kwargs_for_year(year=None, *args, **kwargs):
     return kwargs
 
 
-class NomComFactory(factory.DjangoModelFactory):
+class NomComFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = NomCom
 
@@ -137,7 +139,7 @@ class NomComFactory(factory.DjangoModelFactory):
             for i in range(3):
                 TopicFactory(nomcom=obj)
 
-class PositionFactory(factory.DjangoModelFactory):
+class PositionFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = Position
 
@@ -146,7 +148,7 @@ class PositionFactory(factory.DjangoModelFactory):
     accepting_nominations = True
     accepting_feedback = True
 
-class NomineeFactory(factory.DjangoModelFactory):
+class NomineeFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = Nominee
 
@@ -154,7 +156,7 @@ class NomineeFactory(factory.DjangoModelFactory):
     person = factory.SubFactory(PersonFactory)
     email = factory.LazyAttribute(lambda obj: obj.person.email())
 
-class NomineePositionFactory(factory.DjangoModelFactory):
+class NomineePositionFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = NomineePosition
 
@@ -162,7 +164,7 @@ class NomineePositionFactory(factory.DjangoModelFactory):
     nominee = factory.SubFactory(NomineeFactory)
     state_id = 'accepted'
 
-class FeedbackFactory(factory.DjangoModelFactory):
+class FeedbackFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = Feedback
 
@@ -172,10 +174,10 @@ class FeedbackFactory(factory.DjangoModelFactory):
 
     @factory.post_generation
     def comments(obj, create, extracted, **kwargs):
-        comment_text = factory.Faker('paragraph').generate({})
+        comment_text = Faker().paragraph()
         obj.comments = obj.nomcom.encrypt(comment_text)
 
-class TopicFactory(factory.DjangoModelFactory):
+class TopicFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = Topic
 
@@ -184,7 +186,7 @@ class TopicFactory(factory.DjangoModelFactory):
     accepting_feedback = True
     audience_id = 'general'
 
-class NominationFactory(factory.DjangoModelFactory):
+class NominationFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = Nomination
 
diff --git a/ietf/person/factories.py b/ietf/person/factories.py
index 4cf36b367..2bad1c28c 100644
--- a/ietf/person/factories.py
+++ b/ietf/person/factories.py
@@ -32,7 +32,7 @@ def setup():
     # extraction code, and also don't seem to match the way people with arabic
     # names romanize arabic names.  Exlude those locales from name generation
     # in order to avoid test failures.
-    locales = set( [ l for l in faker.config.AVAILABLE_LOCALES if not (l.startswith('ar_') or l.startswith('sg_')) ] )
+    locales = set( [ l for l in faker.config.AVAILABLE_LOCALES if not (l.startswith('ar_') or l.startswith('sg_') or l=='fr_QC') ] )
     acceptable_fakers = [faker.Faker(locale) for locale in locales]
 setup()
 
@@ -40,7 +40,7 @@ def random_faker():
     global acceptable_fakers
     return random.sample(acceptable_fakers, 1)[0]
 
-class UserFactory(factory.DjangoModelFactory):
+class UserFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = User
         django_get_or_create = ('username',)
@@ -57,7 +57,7 @@ class UserFactory(factory.DjangoModelFactory):
     def set_password(obj, create, extracted, **kwargs): # pylint: disable=no-self-argument
         obj.set_password( '%s+password' % obj.username ) # pylint: disable=no-value-for-parameter
 
-class PersonFactory(factory.DjangoModelFactory):
+class PersonFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = Person
 
@@ -102,7 +102,7 @@ class PersonFactory(factory.DjangoModelFactory):
                 os.unlink(file)
             atexit.register(delete_file, photodst)
 
-class AliasFactory(factory.DjangoModelFactory):
+class AliasFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = Alias
 
@@ -134,7 +134,7 @@ def fake_email_address(n):
             raise RuntimeError("Failed generating a fake email address to fit in Email.address(max_length=%s)"%address_field.max_lenth)
     return address
 
-class EmailFactory(factory.DjangoModelFactory):
+class EmailFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = Email
         django_get_or_create = ('address',)
@@ -147,14 +147,14 @@ class EmailFactory(factory.DjangoModelFactory):
     origin = factory.LazyAttribute(lambda obj: obj.person.user.username if obj.person.user else '')
 
 
-class PersonalApiKeyFactory(factory.DjangoModelFactory):
+class PersonalApiKeyFactory(factory.django.DjangoModelFactory):
     person = factory.SubFactory(PersonFactory)
     endpoint = FuzzyChoice(PERSON_API_KEY_ENDPOINTS)
 
     class Meta:
         model = PersonalApiKey
 
-class PersonApiKeyEventFactory(factory.DjangoModelFactory):
+class PersonApiKeyEventFactory(factory.django.DjangoModelFactory):
     key = factory.SubFactory(PersonalApiKeyFactory)
     person = factory.LazyAttribute(lambda o: o.key.person)
     type = 'apikey_login'
diff --git a/ietf/review/factories.py b/ietf/review/factories.py
index 1812ae5b0..cf66f1ed8 100644
--- a/ietf/review/factories.py
+++ b/ietf/review/factories.py
@@ -6,7 +6,7 @@ from ietf.review.models import ReviewTeamSettings, ReviewRequest, ReviewAssignme
 from ietf.name.models import ReviewTypeName, ReviewResultName
 
 
-class ReviewTeamSettingsFactory(factory.DjangoModelFactory):
+class ReviewTeamSettingsFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = ReviewTeamSettings
 
@@ -31,7 +31,7 @@ class ReviewTeamSettingsFactory(factory.DjangoModelFactory):
         else:
             obj.review_results.set(ReviewResultName.objects.filter(slug__in=('not-ready','right-track','almost-ready','ready-issues','ready-nits','ready')))
 
-class ReviewRequestFactory(factory.DjangoModelFactory):
+class ReviewRequestFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = ReviewRequest
 
@@ -42,7 +42,7 @@ class ReviewRequestFactory(factory.DjangoModelFactory):
     deadline = datetime.datetime.today()+datetime.timedelta(days=14)
     requested_by = factory.SubFactory('ietf.person.factories.PersonFactory')
 
-class ReviewAssignmentFactory(factory.DjangoModelFactory):
+class ReviewAssignmentFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = ReviewAssignment
 
@@ -51,7 +51,7 @@ class ReviewAssignmentFactory(factory.DjangoModelFactory):
     reviewer = factory.SubFactory('ietf.person.factories.EmailFactory')
     assigned_on = datetime.datetime.now()
 
-class ReviewerSettingsFactory(factory.DjangoModelFactory):
+class ReviewerSettingsFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = ReviewerSettings
 
diff --git a/ietf/stats/factories.py b/ietf/stats/factories.py
index 29cda06c5..9c790d181 100644
--- a/ietf/stats/factories.py
+++ b/ietf/stats/factories.py
@@ -6,7 +6,7 @@ from ietf.stats.models import MeetingRegistration
 from ietf.meeting.factories import MeetingFactory
 from ietf.person.factories import PersonFactory
 
-class MeetingRegistrationFactory(factory.DjangoModelFactory):
+class MeetingRegistrationFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = MeetingRegistration
 
diff --git a/ietf/submit/factories.py b/ietf/submit/factories.py
index 143634723..1076434b8 100644
--- a/ietf/submit/factories.py
+++ b/ietf/submit/factories.py
@@ -11,7 +11,7 @@ from ietf.submit.models import Submission, SubmissionExtResource
 from ietf.utils.accesstoken import generate_random_key
 
 
-class SubmissionExtResourceFactory(factory.DjangoModelFactory):
+class SubmissionExtResourceFactory(factory.django.DjangoModelFactory):
     name = factory.Iterator(ExtResourceName.objects.all())
     value = factory.Faker('url')
     submission = factory.SubFactory('ietf.submit.factories.SubmissionFactory')
@@ -19,7 +19,7 @@ class SubmissionExtResourceFactory(factory.DjangoModelFactory):
     class Meta:
         model = SubmissionExtResource
 
-class SubmissionFactory(factory.DjangoModelFactory):
+class SubmissionFactory(factory.django.DjangoModelFactory):
     state_id = 'uploaded'
 
     @factory.lazy_attribute_sequence
diff --git a/requirements.txt b/requirements.txt
index efcf1b9f5..acb30870f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -25,8 +25,10 @@ django-tastypie>=0.14.3         # Django 2.1 will require 0.14.2; Django 3.0 wil
 django-webtest>=1.9.7
 django-widget-tweaks>=1.4.2
 docutils>=0.12,!=0.15
-factory-boy>=2.9.0,<3
-Faker>=0.8.8,!=0.8.9,!=0.8.10   # from factory-boy # Faker 0.8.9,0.8.10 sometimes return string names instead of unicode.
+#factory-boy>=2.9.0,<3
+#Faker>=0.8.8,!=0.8.9,!=0.8.10   # from factory-boy # Faker 0.8.9,0.8.10 sometimes return string names instead of unicode.
+factory-boy>=3
+Faker>=0.8.11   # from factory-boy # Faker 0.8.9,0.8.10 sometimes return string names instead of unicode.
 github3.py>=1.2
 hashids>=1.1.0
 html2text>=2019.8.11