Skip to content

Enabled logging of failed port bind (v2)#490

Open
jvanasco wants to merge 6 commits intoPylons:mainfrom
jvanasco:origin/feature-log_bind_failures
Open

Enabled logging of failed port bind (v2)#490
jvanasco wants to merge 6 commits intoPylons:mainfrom
jvanasco:origin/feature-log_bind_failures

Conversation

@jvanasco
Copy link
Copy Markdown
Contributor

This PR enables logging on failed port binds. See #471

This is a continuation of #489 ; my git checkout got messed up. (While fixing some artifacts from merging to main, I accidentally named a new working branch from origin/feature-log_bind_failures instead of origin's feature-log_bind_failures)

Copy link
Copy Markdown
Member

@kgaughan kgaughan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some immediate comments. I need to take a look over a few other things too though.

Copy link
Copy Markdown
Member

@kgaughan kgaughan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost there! There's some bit of leakage between the tests due to the redundant handling of self.inst and self.insts: by getting everything to use just self.insts, We should end up with everything properly cleaned up between tests. Hopefully I haven't missed anything!

Comment on lines 113 to 116
# most tests only start a single server
if self.inst is not None:
self.inst.close()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need this: the for loop should already be handling that if you update _makeOneWithMulti and _makeOneWithSockets to push onto self.insts. Also, it's potentially closing at least one of the instances twice as the last instance in self.insts created by self._makeOne will be the same as what's in self.inst.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we only really need self.insts, so...

Suggested change
inst = create_server(

Comment on lines 50 to 51
self.insts.append(self.inst)
return self.inst
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...and:

Suggested change
self.insts.append(self.inst)
return self.inst
self.insts.append(inst)
return inst

Nothing outside of the constructor, tearDown, and the _makeOne* methods need to access any of this, so using self.insts for everything keeps cleanup more straighforward.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same again:

Suggested change
inst = create_server(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.insts.append(inst)
return inst

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
inst = create_server(

@@ -91,9 +110,15 @@ def _makeWithSockets(
return self.inst
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return self.inst
self.insts.append(inst)
return inst

Comment on lines +118 to +120
if self.insts:
for inst in self.insts:
inst.close()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This if statement is redundant: the for loop won't run its contents if there's nothing in the list.

Doing self.insts = [] after the loop to prevent bleed-over between tests.

Suggested change
if self.insts:
for inst in self.insts:
inst.close()
for inst in self.insts:
inst.close()
self.insts = []

inst.close()

def test_ctor_app_is_None(self):
self.inst = None
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.inst = None
self.insts = []

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can just drop this line, as the init sets this up already.

Comment on lines +16 to +18
# most tests only start a single server (for cleanup)
inst: Union["BaseWSGIServer", "MultiSocketServer", None]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the rest of the suggested changes, this is now redundant.

@jvanasco
Copy link
Copy Markdown
Contributor Author

Ok. I'm absolutely down to replace everything with self.insts .

I'll get through these on my next free block today.

Comment on lines 291 to 294
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! Spotted one place. Using self.inst here doesn't even really make sense.

Suggested change
inst = WSGIServer(None, _start=False, port=1234)
try:
# Ensure the adjustment was actually applied.
self.assertNotEqual(Adjustments.port, 1234)
self.assertEqual(inst.adj.port, 1234)
finally:
inst.close()

@kgaughan kgaughan linked an issue Mar 23, 2026 that may be closed by this pull request
@jvanasco
Copy link
Copy Markdown
Contributor Author

I'm trying to figure out where/how there is an unclosed socket in warnings.

@jvanasco
Copy link
Copy Markdown
Contributor Author

Ok, the leak is due to #480. I thought this test case could be used to surface it the other day, and I was right.

When creating inst_c fails :

        # a third app should fail the bind to the fist app's host+port
        with self.assertLogs("waitress", level="ERROR") as cm_log:
            with self.assertRaises(OSError) as cm:
                # this line will trigger #480 and leave a socket open
                inst_c = self._makeOne(port=8080)
            self.assertEqual(cm.exception.errno, errno.EADDRINUSE)

it is because the logic in create_server doesn't allow for a proper cleanup.

I added some docstrings to this test; they can be cross-referenced into the other ticket.

I think a future PR to fix that behavior could leverage this test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Log port on failed bind?

2 participants