#34925: refresh_from_db() will not iterate through all of the fields listed in 
the
'fields' parameter.
-------------------------------------+-------------------------------------
     Reporter:  Andrew Cordery       |                    Owner:  Asfand
                                     |  Yar Khan
         Type:  Bug                  |                   Status:  assigned
    Component:  Database layer       |                  Version:  5.0
  (models, ORM)                      |
     Severity:  Normal               |               Resolution:
     Keywords:                       |             Triage Stage:  Accepted
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  1                    |                    UI/UX:  0
-------------------------------------+-------------------------------------

Comment (by Andrew Cordery):

 I searched through the codebase with some admitedly crap regex just
 looking for similar instances of for x in y: ... y.remove(x), and I found
 one other:

 Line 93 of django/core/management/commands/compilemessages.py:

 {{{
         for dirpath, dirnames, filenames in os.walk(".", topdown=True):
             for dirname in dirnames:
                 if is_ignored_path(
                     os.path.normpath(os.path.join(dirpath, dirname)),
 ignore_patterns
                 ):
                     dirnames.remove(dirname)
                 elif dirname == "locale":
                     basedirs.append(os.path.join(dirpath, dirname))
 }}}

 This behavior is explicitly encouraged by python
 https://docs.python.org/3/library/os.html#os.walk, however in my tests
 this will still lead to the same kind of member skipping behavior.  This
 particular block of code would still work correctly as long as there was
 never a 'locale' dirname directly after an ignored dirname in the same
 path.

 For example, imagine the following directory structure, where we wanted to
 extract the two 'locale' dirs (./locale and ./a/locale) and ignore the 'b'
 dir.
 {{{
 root:
   a/
       locale/
   b/
   locale/
 }}}

 If you were to run the following code:

 {{{

 import os
 locale_paths=[]
 for dirpath, dirnames, filenames in os.walk(".", topdown=True):
     # os.walk returns dirnames in arbitrary order
 https://docs.python.org/3/library/os.html#os.listdir,
     # so sort the dirnames to force the skip directory 'b' to come
 directly before the 'locale' directory
     dirnames.sort()
     print(f'Iterating through {dirpath}/{dirnames}')
     for dirname in dirnames:
         path = os.path.join(dirpath, dirname)
         print(f'Examining {path}...', end='')
         if dirname == 'b':
             print(f'Ignoring {path}')
             dirnames.remove(dirname)
         elif dirname == 'locale':
             print(f'Found {path}')
             locale_paths.append(path)
         else:
             print('')
 }}}

 You would get the following output:

 {{{
 Iterating through ./['a', 'b', 'locale']
 Examining ./a...
 Examining ./b...Ignoring ./b
 Iterating through ./a/['locale']
 Examining ./a/locale...Found ./a/locale
 Iterating through ./a/locale/[]
 Iterating through ./locale/[]

 In [33]: locale_paths
 Out[33]: ['./a/locale']
 }}}

 Notice that ./locale is never 'examined' nor does it appear in the
 'locale_paths' output variable. It does however get traversed  as shown by
 the {{{'Iterating through ./locale/[]'}}} message, which makes sense as it
 was not removed from dirnames.  This does mean though that if ./locale/
 had had a 'locale' subdirectory in it, ''that'' directory would have been
 picked up, even though the parent was missed.

 Naturally, changing the line to  {{{for dirname in list(dirnames)}}} also
 completely resolves this issue.

 Not sure any of that matters or is useful to you guys, but it was
 interesting to me.  Moral of the story is don't ever mutate a list we are
 iterating through, even if the python docs seem to imply that you can!  I
 think their docs should be amended to specifically state that you should
 NOT mutate it during the initial iteration, and that instead you should
 iterate through a copy (ex list(dirnames)) or only mutate it once you have
 examined every member of the list that you are interested in, right before
 your code exits.

-- 
Ticket URL: <https://code.djangoproject.com/ticket/34925#comment:3>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/0107018b669a372c-5ce56cd6-8a7b-4897-99da-029fe2486a38-000000%40eu-central-1.amazonses.com.

Reply via email to